Gérez la pagination des photos d'users Facebook

Je travaille sur une fonctionnalité pour une application iOS qui permettra à son user de choisir une photo de sa galerie Facebook.

J'ai reçu la request initiale pour que les photos fonctionnent – cela renvoie un petit nombre de photos et un lien vers les lots suivants et précédents. Mon problème est que je ne sais pas quelle est la bonne façon de gérer cette pagination; J'ai passé beaucoup de time à essayer de le find sur Google ou de find une réponse dans la documentation de Facebook, mais ce n'est que de la foutaise (c'est-à-dire aucune aide).

Pourriez-vous jeter un oeil à la méthode qui est censée traiter cette request et m'expliquer comment append le rest des photos à la masortingce modifiable 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); } }]; } 

Oh, et oui – j'ai essayé d'utiliser grabKit mais j'ai décidé de ne pas passer plus de time à essayer de le mettre en place – j'ai suivi les instructions à la lettre mais ça aurait quand même entrainé des erreurs.

J'ai utilisé un appel de fonction récursive pour résoudre ce problème et j'ai fixé une limite basse de 10 pour tester la fonctionnalité.

 -(void)facebookCall { [self getFBFriends:@"me/friends?fields=name,picture.type(large)&limit=10"]; } -(void)getFBFriends:(NSSsortingng*)url { [FBRequestConnection startWithGraphPath:url completionHandler:^(FBRequestConnection *connection, id result, NSError *error) { if (!error) { [self parseFBResult:result]; NSDictionary *paging = [result objectForKey:@"paging"]; NSSsortingng *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 subssortingngFromIndex:27]); [self getFBFriends:[next subssortingngFromIndex: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); } 

Ceci est principalement basé sur mes searchs effectuées en utilisant la méthode d'essai et d'erreur car la documentation de Facebook n'était pas utile du tout. Je suis heureux d'apprendre une meilleure méthode pour le faire 🙂

Nous pouvons ensuite utiliser les appels de l'explorateur de graphes dans le code du template:

 NSSsortingng *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 accepte et répond dans JSON.

Obtention des albums et des photos d'un user – le problème de la pagination

Une fois qu'un user est connecté, sa session est traitée en coulisses par l'API de Facebook, donc il n'y a pas de raison de s'inquiéter, nous pouvons simplement effectuer les requests spécifiques que nous voulons.

Pour get datatables d'albums pour un user, placez cette string dans l'explorateur de graphes:

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

Cela requestra Fb pour le nombre de photos dans chaque album et leurs identifiants. Notez que le 1er niveau de la réponse JSON contient l'ID de l'user ainsi que le tableau "albums" qui contient le tableau "data" – c'est le tableau des albums qui nous intéressent.

Ayant les identifiants de chaque album, nous pouvons explorer leurs photos. L'appel ci-dessous aura des liens vers la photo source de chaque album et sa miniature:

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

où est l'identifiant de l'album dont vous voulez get les photos.

Le problème initial est que, comme il peut y avoir beaucoup de photos dans un album, essayer de les get en une seule fois est probablement une mauvaise idée – c'est pourquoi les développeurs de Facebook ont ​​introduit la pagination dans ces appels. Cela signifie que vous pouvez définir une limite pour le nombre de données de photos que vous obtenez en un seul appel et que vous utilisez ensuite un "slider" pour les lots suivants / précédents que vous souhaitez get, avec les sliders qui vous sont donnés dans chaque appel. Le principal problème est de traiter de teldatatables paginées. Si nous regardons datatables returnnées dans notre précédent appel, nous pouvons voir qu'il y a une section "pagination" avec "sliders" (qui contient "avant" et "après") et "suivant". La touche "next" est un lien qui ressemble beaucoup à notre string d'appel utilisée dans l'Explorateur de Graph – et il se termine par le slider "after"; nous pourrions penser, alors, qu'il est possible d'append simplement le slider "après" à notre string d'appel

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

et alimentez cela dans l'explorateur de graphes. Nan! Pour une raison quelconque, cela ne fonctionnera pas comme prévu – il nous dirige toujours vers le premier lot au lieu du suivant. Cependant, le lien "suivant" fonctionne toujours, il est donc possible d'en utiliser des parties à la place de l'appel que nous avons fait à l'explorateur de graphes. Ainsi l'appel pour get les photos:

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

devient:

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

De plus, cela fonctionne toujours après avoir été ajouté avec & after =:

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

Ainsi, il est facile d'get simplement la valeur de "next" dans chaque appel d'un lot et de l'append à la string ci-dessus pour le prochain appel.

Voici l'extrait de la version finale du code:

 NSSsortingng *const FACEBOOK_GRAPH_LIST_ALBUMS = @"me?fields=albums.fields(count,id,name)"; NSSsortingng *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) { NSSsortingng *albumId = [currentAlbum valueForKey:@"id"]; [self getCurrentUserFacebookPhotosWithAlbum:albumId afterCursor:nil failure:^(NSError *error) { failureHandler(error); }]; } successHandler(); } else { failureHandler(error); } }]; } - (void) getCurrentUserFacebookPhotosWithAlbum:(NSSsortingng *) albumId afterCursor:(NSSsortingng *) afterCursor failure:(void (^) (NSError *error))failureHandler { if (afterCursor == nil) { afterCursor = @""; } NSSsortingng *fbGraphCall = [NSSsortingng ssortingngWithFormat:@"%@%@%@", 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 NSSsortingng *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:(NSSsortingng *) albumId { for (NSDictionary *albumData in currentUsersFacebookAlbums) { if ([albumId isEqualToSsortingng:[albumData valueForKey:@"id"]] && [currentUsersFacebookAlbums indexOfObject:albumData] == [currentUsersFacebookAlbums count] - 1) { return YES; } } return NO; } 

Pour la pagination de facebook, je suggère d'utiliser Apple native class comme

Utilisez la variable nextPageURL pour mettre en cache l'URL suivante de la réponse JSON et l'assigner à la string url lors de la requête suivante de l'API si nextPageURL n'est pas nul et utilisez le code suivant:

  if (self.nextPageURL) { // urlSsortingng is the first time formulated url ssortingng urlSsortingng = self.nextPageURL; } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithSsortingng:urlSsortingng]]; [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. } } }];