0

I have a method that makes a specific request to an api, that I would like to return the result from. Here is my method:

-(NSDictionary *) getProfileDataForUser:(NSString *)user_id {
NSURL *getProfileDataRequestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@users/profile.php?user_id=%@", _apiRootUrl, user_id]];
NSMutableURLRequest *getProfileDataRequest = [NSMutableURLRequest requestWithURL:getProfileDataRequestURL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0f];
[getProfileDataRequest setHTTPMethod:@"GET"];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:getProfileDataRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
    error = nil;
    id jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
    if (jsonData != nil && error == nil) {
        NSDictionary *responseDict = jsonData;
        if ([responseDict objectForKey:@"user"]) {
            // this is a dictionary
            return [responseDict objectForKey:@"user"];
        } else {
            return responseDict;
        }
    }
    else {
        return @{};
    }
}];

}

The trouble is I get multiple semantic issues:

Error on sendAsynchronousRequest method:

Incompatible block pointer types sending 'id (^)(NSURLResponse *__strong, NSData *__strong, NSError *__strong)' to parameter of type 'void (^)(NSURLResponse *__strong, NSData *__strong, NSError *__strong)'

Error on returns:

Return type 'NSDictionary *' must match previous return type 'id' when block literal has unspecified explicit return type

I have tried changing the return type of my method to id, and just returning jsonData but I still get the first semantic issue. How can I make this work?

inorganik
  • 24,255
  • 17
  • 90
  • 114
  • You can't return a dictionary (or anything else) from a method that has an asynchronous call in it. You could accomplish what you want by using a delegate method that you call from the completion block. – rdelmar Aug 12 '14 at 15:28
  • Finally found a duplicate... couldn't find at time of asking. http://stackoverflow.com/questions/12352901/getting-data-out-of-the-nsurlresponse-completion-block – inorganik Aug 12 '14 at 15:33
  • See http://stackoverflow.com/questions/21730065/return-method-is-failing-in-a-completion-block/21730687#21730687, which includes links to other similar questions, too. – Rob Aug 12 '14 at 15:33

2 Answers2

2

the url request is asynchronous so the method needs to use a block/callback to return the result:

- (void) getProfileDataForUser:(NSString *)user_id withCallback:(void (^)(NSDictionary *jsonData))callback {
    NSURL *getProfileDataRequestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@users/profile.php?user_id=%@", _apiRootUrl, user_id]];
    NSMutableURLRequest *getProfileDataRequest = [NSMutableURLRequest requestWithURL:getProfileDataRequestURL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0f];
    [getProfileDataRequest setHTTPMethod:@"GET"];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:getProfileDataRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        error = nil;
        id jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
        callback(jsonData);
    }];
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
inorganik
  • 24,255
  • 17
  • 90
  • 114
0

In your block, remove the return statements

Explaination

The completion block is not supposed to return anything, thats why you're getting semantic issue

Incompatible: Sending 'id (^)(NSURLResponse....' to parameter of type 'void (^)(NSURLResponse...'
                        ^                                                ^
                        |-----------------is different from--------------|

Edit:

If you want to return NSDictionary from completion handler refactor the code as follows:

-(NSDictionary *) getProfileDataForUser:(NSString *)user_id {
    NSURL *getProfileDataRequestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@users/profile.php?user_id=%@", _apiRootUrl, user_id]];
    NSMutableURLRequest *getProfileDataRequest = [NSMutableURLRequest requestWithURL:getProfileDataRequestURL cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10.0f];
    [getProfileDataRequest setHTTPMethod:@"GET"];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    // This holds the response to be returned from this method
    __block NSDictionary *rDict = nil;

    [NSURLConnection sendSynchronousRequest:getProfileDataRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        error = nil;
        id jsonData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
        if (jsonData != nil && error == nil) {
            NSDictionary *responseDict = jsonData;
            if ([responseDict objectForKey:@"user"]) {
                // this is a dictionary
                rDict = [responseDict objectForKey:@"user"];
            } else {
                rDict = responseDict;
            }
        }
        else {
            rDict = @{};
        }
    }];

    return rDict;
}

Edit

The above code will only work if it is a synchronous request.

bhargavg
  • 1,383
  • 9
  • 12
  • The method will have an error if it doesn't return an `NSDictionary`. I need to return a dictionary. – inorganik Aug 12 '14 at 15:18
  • It's an asynchronous request- the return value you have in your method will be `null` because when it fires the response will not have come back yet from the request. – inorganik Aug 12 '14 at 15:25
  • This will not work, because the getProfileDataForUser: method will return immediately, before the asynchronous call returns (so rDict will be nil). – rdelmar Aug 12 '14 at 15:26
  • oops, sorry @inorganik, my bad. Missed that part. Will change it – bhargavg Aug 12 '14 at 15:27
  • 1
    @inorganik, You have two options then. 1. Create callbacks using delegates 2. Pass in a block, along with `user_id`, which you can execute in `sendAsynchronousRequest` callback – bhargavg Aug 12 '14 at 15:31
  • @bhargavg Yep, that's a good approach. Keep it asynchronous and use block parameters (or delegates, if that's the way you roll)! – Rob Aug 12 '14 at 15:38