处理Facebook用户照片的分页

时间:2013-12-09 17:28:25

标签: ios objective-c facebook-graph-api pagination facebook-ios-sdk

我正在研究iOS应用的一项功能,该功能可让用户从其Facebook图库中选择一张照片。

我已经获得了让照片正常工作的初始请求 - 它确实返回了少量照片以及指向下一批和之前批次的链接。我的问题是我不知道处理这种分页的正确方法是什么;我花了很长时间试图谷歌或在Facebook的文档中找到答案,但它只是垃圾(即没有任何帮助)。

你能看看应该处理这个请求的方法,并向我解释如何将其余的照片添加到usersFacebookPhotos可变数组中吗?

NSMutableArray *usersFacebookPhotos;

- (void) getUserPhotoAlbumsWithSuccess:(void (^) (bool))successHandler failure:(void (^) (NSError *error))failureHandler {

    usersFacebookPhotos = (NSMutableArray *)[[NSArray alloc] init];

    FBRequest *fbRequest = [FBRequest requestWithGraphPath:@"me?fields=photos.fields(picture,source)" parameters:nil HTTPMethod:@"GET"];
    [fbRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

        if (!error) {

            NSLog(@"got the initial batch");
            // process the next batch of photos here
        }
        else {

            NSLog(@"error: %@", error);
        }
    }];
}

哦,是的 - 我尝试使用了grabKit,但决定不再花时间尝试设置它 - 我按照说明书写了这封信,但它仍会引发错误。

3 个答案:

答案 0 :(得分:3)

我已使用递归函数调用来解决此问题,并且我已将下限设置为10来测试功能。

-(void)facebookCall {   
    [self getFBFriends:@"me/friends?fields=name,picture.type(large)&limit=10"];
}

-(void)getFBFriends:(NSString*)url {
    [FBRequestConnection startWithGraphPath:url
                      completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
                          if (!error) {
                              [self parseFBResult:result];

                              NSDictionary *paging = [result objectForKey:@"paging"];
                              NSString *next = [paging objectForKey:@"next"];

                              // skip the beginning of the url https://graph.facebook.com/
                              // there's probably a more elegant way of doing this

                              NSLog(@"next:%@", [next substringFromIndex:27]);

                              [self getFBFriends:[next substringFromIndex:27]];

                          } else {
                              NSLog(@"An error occurred getting friends: %@", [error localizedDescription]);
                          }
                      }];
}

-(void)parseFBResult:(id)result {

    NSLog(@"My friends: %@", result);

    NSArray *data = [result objectForKey:@"data"];
    int j = 0;
    for(NSDictionary *friend in data){
        NSDictionary *picture = [friend objectForKey:@"picture"];
        NSDictionary *picData = [picture objectForKey:@"data"];
        NSLog(@"User:%@, picture URL: %@", [friend objectForKey:@"name"], [picData objectForKey:@"url"]);
        j++;
    }
    NSLog(@"No of friends is: %d", j);

}

答案 1 :(得分:0)

这主要是基于我使用试错法进行的研究,因为Facebook的文档根本没用。我很高兴知道一种更好的方法:)

然后我们可以在模板代码中使用Graph Explorer中的调用:

NSString *yourCall = @”YourGraphExplorerCall”;

FBRequest *fbRequest = [FBRequest requestWithGraphPath:yourCall parameters:nil HTTPMethod:@"GET"];
[fbRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

if (!error) {

    NSDictionary *jsonResponse = (NSDictionary *) result;
    // Do your stuff with the JSON response
}
else {

    failureHandler(error);
}
}];

Facebook Graph接受并以JSON回复。

获取用户的相册和照片 - 分页问题

用户登录后,他们的会话由Facebook API在幕后处理,因此无需担心,我们只能执行我们想要的特定请求。

要获取用户的专辑数据,请将此字符串放入Graph Explorer:

me?fields=albums.fields(count,id)

这会向Fb询问每张专辑中的照片数量及其ID。请注意,JSON回复的第1级包含用户的ID以及包含“data”数组的“albums”数组 - 这是我们感兴趣的实际相册的数组。

拥有每张专辑的ID,我们可以探索他们的照片。以下电话将获得每张专辑的源照片及其缩影的链接:

<album_id>?fields=photos.fields(source,picture)

其中是您想要获得照片的相册的实际ID。

最初的问题是,由于专辑中可能有很多照片,试图将它们一次性推出可能是一个坏主意 - 这就是为什么Facebook开发人员将这些调用引入这些调用的原因。这意味着您可以设置一次调用中获得的照片数据数量的限制,然后使用“光标”进行下一个/上一个批次,您将获得所述游标。每次通话。 主要问题是处理这种分页数据。如果我们查看前一次调用中返回的数据,我们可以看到有一个“分页”部分,其中包含“游标”(包含“之前”和“之后”)和“下一个”。 “next”键是一个链接,看起来非常类似于Graph Explorer中使用的调用字符串 - 它以“after”光标结束;那么我们可以认为,可以简单地将“after”光标附加到我们的调用字符串

<album_id>?fields=photos.fields(source,picture)&after=<after_cursor>

并将其输入Graph Explorer。不!由于某种原因,这将无法按预期工作 - 它仍然指导我们到第一批而不是下一批。 但是,“下一个”链接仍然有效,因此可以使用它的一部分而不是我们对Graph Explorer进行的调用。因此,获得照片的电话:

<album_id>?fields=photos.fields(source,picture)

变为:

<album_id>/photos?fields=source%2Cpicture&limit=25

此外,它在附加&amp; after =:

后仍然有效
<album_id>/photos?fields=source%2Cpicture&limit=25&after=

因此,在批次的每次调用中简单地获取“next”的值很容易,并将其附加到上面的字符串以进行下一次调用。

以下是代码最终版本的片段:

NSString *const FACEBOOK_GRAPH_LIST_ALBUMS = @"me?fields=albums.fields(count,id,name)";
NSString *const FACEBOOK_GRAPH_LIST_ALBUM_PHOTOS = @"/photos?fields=source%2Cpicture&limit=25&after=";
NSArray *currentUsersFacebookAlbums;

- (void) getUserPhotosWithSuccess:(void (^) ())successHandler failure:(void (^) (NSError *error))failureHandler {

    FBRequest *fbRequest = [FBRequest requestWithGraphPath:FACEBOOK_GRAPH_LIST_ALBUMS parameters:nil HTTPMethod:@"GET"];
    [fbRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

        if (!error) {

            NSDictionary *jsonResponse = (NSDictionary *) result;
            currentUsersFacebookAlbums = (NSArray *) [[jsonResponse valueForKey:@"albums"] valueForKey:@"data"];

            for (NSDictionary *currentAlbum in currentUsersFacebookAlbums) {

                NSString *albumId = [currentAlbum valueForKey:@"id"];
                [self getCurrentUserFacebookPhotosWithAlbum:albumId afterCursor:nil failure:^(NSError *error) {
                    failureHandler(error);
                }];
            }

            successHandler();
        }
        else {

            failureHandler(error);
        }
    }];
}

- (void) getCurrentUserFacebookPhotosWithAlbum:(NSString *) albumId afterCursor:(NSString *) afterCursor failure:(void (^) (NSError *error))failureHandler {

    if (afterCursor == nil) {

        afterCursor = @"";
    }

    NSString *fbGraphCall = [NSString stringWithFormat:@"%@%@%@", albumId, FACEBOOK_GRAPH_LIST_ALBUM_PHOTOS, afterCursor];

    FBRequest *fbRequest = [FBRequest requestWithGraphPath:fbGraphCall parameters:nil HTTPMethod:@"GET"];
    [fbRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {

        if (!error) {

            NSDictionary *jsonResponse = (NSDictionary *) result;
            NSArray *currentPhotoBatch = (NSArray *) [jsonResponse valueForKey:@"data"];

            // Go through the currently obtained batch and add them to the returned mutable array
            for (NSDictionary *currentPhoto in currentPhotoBatch) {

                [[CurrentUserDataHandler sharedInstance] addFacebookPhoto:currentPhoto];
            }

            // If there's a "next" link in the response, recur the method on the next batch...
            if ([[jsonResponse valueForKey:@"paging"] objectForKey:@"next"] != nil) {

                // ...by appending the "after" cursor to the call
                NSString *afterCursor = [[[jsonResponse valueForKey:@"paging"] valueForKey:@"cursors"] valueForKey:@"after"];
                [self getCurrentUserFacebookPhotosWithAlbum:albumId afterCursor:afterCursor failure:^(NSError *error) {
                    failureHandler(error);
                }];
            }

            if ([[jsonResponse valueForKey:@"paging"] objectForKey:@"next"] != nil && [self isLastAlbum:albumId]) {

                [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_FACEBOOK_PHOTOS object:nil];
            }
        }
        else {

            failureHandler(error);
        }
    }];
}

- (bool) isLastAlbum:(NSString *) albumId {

    for (NSDictionary *albumData in currentUsersFacebookAlbums) {

        if ([albumId isEqualToString:[albumData valueForKey:@"id"]] && [currentUsersFacebookAlbums indexOfObject:albumData] == [currentUsersFacebookAlbums count] - 1) {

            return YES;
        }
    }

    return NO;
}

答案 2 :(得分:0)

对于facebook的分页,我建议使用Apple本地类作为

如果nextPageURL不是nil,则使用nextPageURL变量从JSON响应中缓存下一个url,并在下一个api请求中分配给​​url字符串并使用以下代码:

 if (self.nextPageURL) {
    // urlString is the first time formulated url string
    urlString = self.nextPageURL;
}
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlString]];
[NSURLConnection sendAsynchronousRequest:request queue:networkQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    if (error) {
        DDLogVerbose(@"FACEBOOK:Connection error occured: %@",error.description);
    }else{
        isRequestProcessing = NO;
        NSDictionary *resultData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
        DDLogVerbose(@"parsed data is %@",resultData);
        self.nextPageURL = resultData[@"paging"][@"next"];

        // do your customisation of resultData here.
        }
    }
}];