1
votes

I have this bool method that returns a yes or no for an inputted string. I'm successfully able to return a YES or a NO, but I cannot seem to able to make a network connection and return a YES or a NO depending on the server's response. I tried using __block and I don't feel like that will wait for the web request to finish, is there a way to return YES or NO in the success block without it giving me the error:

Incompatible block pointer types sending 'BOOL(^)(NSURLSessionTask*__strong, NSError __strong' to parameter of the type 'void(^)(NSURLSessionTask...)

-(BOOL)customResponseForString:(NSString *)text {

  __block BOOL response_available;

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    [manager.responseSerializer setAcceptableContentTypes:[NSSet setWithObject:@"text/plain"]];
    [manager GET:[NSString stringWithFormat:@"http://example.com/response.php?input=%@", text] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {

        NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSUTF8StringEncoding error:nil];
        response_available = (BOOL)response[@"response_available"];
        if (response_available) {
            [session sendTextSnippet:response[@"response"] temporary:NO scrollToTop:NO dialogPhase:@"Summary"];
        } else {
            response_available = NO;
        }

        [session sendTextSnippet:[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] temporary:NO scrollToTop:NO dialogPhase:@"Summary"];

        [session sendRequestCompleted];

    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        //return NO;
    }];
 });

return response_available;

}
2
You can't do what you want here. The call to return at the end of the customResponseForString: method will be made long before any of the code in the dispatch_async is even started let alone long before either the success or failure blocks are ever called.rmaddy
Is there any work around you can help me think of... I'm stuck...Sal B.

2 Answers

1
votes

Your block definition syntax is probably erroneous, because you can definitely return a BOOL along other parameters in a block.

- (void)fetchCurrentUserWithCompletion:(void (^)(BOOL success, User *user))completion;

This method would be called like this:

[self.userProfileController fetchCurrentUserWithCompletion:^(BOOL success, User *user) {
    if (success) {
        NSLog(@"Current User Name: %@", user.fullName);
    }
}];

If you use AFNetworking, check the AFHTTPRequestOperation object that handle completionBlocks:

[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    User *user = [self userFromResponseObject:responseObject];   
    if (completion) completion(YES, user);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    if (completion) completion(NO, user);
}];
1
votes

Because you are implicitly initializing response_available to NO and then using an async GCD call, your method as written will always immediately return NO without waiting for the request to finish. Note: switching to dispatch_sync won't help either because AFNetworking will queue the GET request asynchronously either way.


Best Approach

Add a completion block argument to customResponseForString:. Then simply execute your completion block in the success or failure blocks of the AFHTTPRequestOperation.


Workable Approach (use caution!)

It is possible to make customResponseForString: wait for a response to the network request, but you will have significant issues if it is ever called from the main thread.

First you create a dispatch group and tell it you are starting some long-running work:

dispatch_group_t networkGroup = dispatch_group_create();
dispatch_group_enter(networkGroup);

Then you need to make your network request and when it completes tell the group that the work is finished with dispatch_group_leave():

[manager GET:[NSString stringWithFormat:@"http://example.com/response.php?input=%@", text] parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {

    NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseObject options:NSUTF8StringEncoding error:nil];
    response_available = (BOOL)response[@"response_available"];
    if (response_available) {
        [session sendTextSnippet:response[@"response"] temporary:NO scrollToTop:NO dialogPhase:@"Summary"];
    } else {
        response_available = NO;
    }

    [session sendTextSnippet:[[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] temporary:NO scrollToTop:NO dialogPhase:@"Summary"];

    [session sendRequestCompleted];

    dispatch_group_leave(networkGroup);

} failure:^(NSURLSessionDataTask *task, NSError *error) {
    response_available = NO;
    dispatch_group_leave(networkGroup);
}];

Before your original method returns, tell it to wait for the entire group to finish processing:

dispatch_group_wait(networkGroup, DISPATCH_TIME_FOREVER);
return response_available;

You could adjust this time interval as needed or leave it at DISPATCH_TIME_FOREVER to let the network request time out on its own.