Método que devuelve el valor del bloque asíncrono con FacebookSDK

Lo que trato de hacer es un contenedor de Facebook para el SDK de iOS de Facebook. Básicamente, la idea es que mi ViewController no haga nada más que mostrar ex. mis amigos que serán adquiridos con una simple llamada como

self.friends = [FacebookWrapper myFriends]; [self.tableView reloadData]; 

El método myFriends de my wrapper debería verse así

 + (NSArray *)myFriends { __block NSArray *friends = nil; [FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) { if(FB_ISSESSIONOPENWITHSTATE(status)) { [FBRequestConnection startForMyFriendsWithCompletionHandler:^(FBRequestConnection *connection, id data, NSError *error) { CFRunLoopStop(CFRunLoopGetCurrent()); if(error) { return; } NSArray *friendsData = (NSArray *)[data data]; NSMutableArray *fbFriends = [NSMutableArray array]; for(id friendData in friendsData) { Friend *friend = [Friend friendWithDictionary:friendData]; fbFriends addObject:friend]; } friends = [NSArray arrayWithArray:fbFriends]; }]; CFRunLoopRun(); } }]; return friends; } 

El problema es que openActiveSessionWithReadPermissions y startForMyFriendsWithCompletionHandler son bloques asíncronos, por lo que el método vuelve antes de que los bloques completen su tarea.

Cualquier ayuda sería muy apreciada.

Creé un contenedor similar en el pasado y mi enfoque estaba pasando un "bloque de finalización" al llamar al método de mi contenedor; este bloque de finalización se activa una vez que todas las llamadas asincrónicas se ejecutan y recibe los datos que su método devolvería en un escenario sincrónico (en su caso, la matriz de amigos).

Para ilustrar, podría tener su método "myFriends" networkingefinido como:

+ (void)myFriendsWithCompletionBlock:(void (^)(NSArray *friends))completionBlock;

Luego, en la implementación, justo después de los friends = [NSArray arrayWithArray:fbFriends]; línea, agregarías esto:

 if (completionBlock != nil) { completionBlock(friends); } 

… y elimine la statement de return al final.

Finalmente, en su controller de vista (o cualquier object que use el método, haría algo como esto:

 [FacebookWrapper myFriendsWithCompletionBlock:^(NSArray *friends){ // do what you need to do with the friends array }]; 

Por supuesto, esto sigue siendo asíncrono, pero no hay forma de evitarlo ya que así fue como se construyó el SDK de Facebook (y, para ser justos, esta es probablemente la mejor manera de hacerlo) ¡esperar que las requestes para terminar sincrónicas sean terribles!)

Edit: noté que también está volviendo del método del envoltorio en caso de que falle; En esa situación, en lugar de regresar harías algo como esto:

 if (completionBlock != nil) { completionBlock(nil); } 

Eso causaría que la matriz de friends sea nil cuando se llame a su bloque de finalización, entonces puede tratar ese error allí, sin embargo, parece apropiado para usted.

Espero que esto haya ayudado!

Si está enviando un bloque asíncrono, puede comunicarse con su subclass UIViewController volviendo a llamarlo:

 [self someSelectorWithCallbackData:stuffWhichYouWantToGiveBack]; 

Esto se llamará a self para que el bloque lo capture, y así funcionará como se esperaba. Desde el método correspondiente, puede actualizar la vista / recargar la vista de tabla / bailar una plantilla según sea necesario.

Dependiendo del context, es posible que deba __block self , por ejemplo,

 __block UIViewController *bsself = self; 

Pero si hace esto último, tenga cuidado de evitar un ciclo de retención (las herramientas de compilation y análisis son bastante buenas para señalar esto).

Cree que necesita usar un service web protol @class;

 @protocol WebserviceDelegate @optional -(void)webservice:(Webservice *)webservice didFetchPosts:(NSArray *)posts; -(void)webservice:(Webservice *)webservice didFetchComments:(NSArray *)comments forPostID:(NSString *)postID launchComments:(BOOL)launch; -(void)webservice:(Webservice *)webservice didLoginWithUser:(User *)user; -(void)webservice:(Webservice *)webservice didVoteWithSuccess:(BOOL)success forObject:(id)object direction:(BOOL)up; @end @interface Webservice : NSObject { __weak id <WebserviceDelegate> delegate; } //Delegate @property (weak) id <WebserviceDelegate> delegate; -(void)getHomepage { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSURLResponse *response; NSError *error; // Create the URL Request NSMutableURLRequest *request = [Webservice NewGetRequestForURL:[NSURL URLWithString:@"https://www.hnsearch.com/bigrss"]]; // Start the request NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; //Handle response //Callback to main thread if (responseData) { NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSStringEncodingConversionAllowLossy]; if (responseString.length > 0) { dispatch_async(dispatch_get_main_queue(), ^{ [self parseIDsAndGrabPosts:responseString]; }); } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } }); } -(void)parseIDsAndGrabPosts:(NSString *)parseString { // Parse String and grab IDs NSMutableArray *items = [@[] mutableCopy]; NSArray *itemIDs = [parseString componentsSeparatedByString:@"<hnsearch_id>"]; for (int xx = 1; xx < itemIDs.count; xx++) { NSString *idSubString = itemIDs[xx]; [items addObject:[idSubString substringWithRange:NSMakeRange(0, 13)]]; } // Send IDs back to HNSearch for Posts dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSURLResponse *response; NSError *error; // Create Request String NSString *requestString = @"http://api.thriftdb.com/api.hnsearch.com/items/_bulk/get_multi?ids="; for (NSString *item in items) { requestString = [requestString stringByAppendingString:[NSString stringWithFormat:@"%@,", item]]; } // Create the URL Request NSMutableURLRequest *request = [Webservice NewGetRequestForURL:[NSURL URLWithString:requestString]]; // Start the request NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; //Handle response //Callback to main thread if (responseData) { NSArray *responseArray = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:&error]; if (responseArray) { NSMutableArray *postArray = [@[] mutableCopy]; for (NSDictionary *dict in responseArray) { [postArray addObject:[Post postFromDictionary:dict]]; } NSArray *ordenetworkingPostArray = [self orderPosts:postArray byItemIDs:items]; dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:ordenetworkingPostArray]; // Update Karma for User if ([HNSingleton shanetworkingHNSingleton].User) { [self reloadUserFromURLString:[NSString stringWithFormat:@"https://news.ycombinator.com/user?id=%@", [HNSingleton shanetworkingHNSingleton].User.Username]]; } }); } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } } else { dispatch_async(dispatch_get_main_queue(), ^{ [delegate webservice:self didFetchPosts:nil]; }); } }); }