Method returning value from asynchronous block with FacebookSDK

荒凉一梦 提交于 2019-12-18 05:15:05

问题


What I am trying to do is a Facebook wrapper for the Facebook iOS SDK. Basically the idea is that my ViewController should do nothing but showing ex. my friends that will be acquired with a simple call like

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

My wrapper myFriends method should look like this

+ (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;
}

The issue is that the openActiveSessionWithReadPermissions and startForMyFriendsWithCompletionHandler are asynchronous blocks so the method returns before the blocks complete their task.

Any help would be much appreciated.


回答1:


I created a similar wrapper in the past and my approach was passing a "completion block" when calling my wrapper method; this completion block is then triggered once all the asynchronous calls are done running, and it receives whatever data your method would return in a synchronous scenario (in your case, the array of friends).

To illustrate - you could have your "myFriends" method redefined as:

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

Then in the implementation, right after the friends = [NSArray arrayWithArray:fbFriends]; line, you would add this:

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

... and remove the return statement at the end.

Finally, on your view controller (or any object using the method, you would do something like this:

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

Of course, this is still asynchronous - but there's no way around since that's how the Facebook SDK was build (and, to be fair, this is probably the best way to do it - waiting for requests to finish synchronous would be terrible!)

Edit: I noticed you're also returning from the wrapper method in case it fails; in that situation, instead of returning you would do something like this:

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

That would cause the friends array to be nil when your completion block is called - you can then treat that error there however seems appropriate to you.

Hope this helped!




回答2:


If you are dispatching an asynchronouos block, you can communicate with your UIViewController subclass by calling back to it:

[self someSelectorWithCallbackData:stuffWhichYouWantToGiveBack];

This will call self to get captured by the block, and so will work as expected. From the relevant method you can refresh the view / reload the tableview / dance a jig as required.

Depending on the context, you may need to __block scope self, eg

__block UIViewController *bsself = self;

But if you do the latter, be careful to avoid a retain loop (the build and analysis tools are fairly good at pointing this out).




回答3:


Think you need to use a protol @class Webservice;

@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 *orderedPostArray = [self orderPosts:postArray byItemIDs:items];

                dispatch_async(dispatch_get_main_queue(), ^{
                    [delegate webservice:self didFetchPosts:orderedPostArray];

                    // Update Karma for User
                    if ([HNSingleton sharedHNSingleton].User) {
                        [self reloadUserFromURLString:[NSString stringWithFormat:@"https://news.ycombinator.com/user?id=%@", [HNSingleton sharedHNSingleton].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];
            });
        }


    });
}


来源:https://stackoverflow.com/questions/15134394/method-returning-value-from-asynchronous-block-with-facebooksdk

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!