0

enter image description hereI am having this problem, why can I not RETURN at value :

- (NSArray *)SQLRetrieve:(NSString *)barID {

    self.client = [MSClient clientWithApplicationURLString:@"https://outnight-mobile.azure-mobile.net/" applicationKey:@"okYeRGfBagYrsbkaqWIRObeDtktjkF10"];
    [self.client invokeAPI:@"photos"
                      body:barID
                HTTPMethod:@"POST"
                parameters:nil
                   headers:nil
                completion:^(id result, NSHTTPURLResponse *response, NSError *error) {
                    if (error) {
                        NSLog(@"Error %@", error );
                    } else
                    {
                        NSString *string = [NSString stringWithFormat:@"%@", [result objectForKey:@"rows"]];
                        NSString *stringWithoutbracketsend = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
                        NSString *stringWithoutbracketsfront = [stringWithoutbracketsend stringByReplacingOccurrencesOfString:@"(" withString:@""];
                        NSString *completion = [stringWithoutbracketsfront stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
                        NSString *newStr = [completion substringFromIndex:1];
                        NSString *finalstring = [newStr substringToIndex:newStr.length-(newStr.length>0)];
                        //NSLog(@"%@", finalstring);
                        [MyArray addObject:finalstring];
                        NSLog(@"%@", [MyArray objectAtIndex:0]);
                        return finalstring;
                    }
                }];

It comes up with an error after. I have attached an image of the error. just completely and utterly lost - can someone impress us by telling us what the problem is

3 Answers3

2

You are attempting to return a value from inside the completion block, but that block has a void return type. That's why the compiler is complaining.

If you want this method to return values from within this (presumably) asynchronously executed completion block, you shouldn't try to return the value from within the block, but rather you should employ a asynchronous completion block pattern yourself:

- (void)SQLRetrieve:(NSString *)barID completion:(void (^)(NSString *string, NSError *error))completion {

    self.client = [MSClient clientWithApplicationURLString:@"https://outnight-mobile.azure-mobile.net/" applicationKey:@"..."];
    [self.client invokeAPI:@"photos"
                      body:barID
                HTTPMethod:@"POST"
                parameters:nil
                   headers:nil
                completion:^(id result, NSHTTPURLResponse *response, NSError *error) {
                    if (error) {
                        if (completion)
                            completion(nil, error);

                        // or, you might want to explicitly dispatch that completion block back to the main queue:
                        //
                        // if (completion)
                        //     dispatch_async(dispatch_get_main_queue(), ^{
                        //         completion(nil, error);
                        //     });
                    } else {
                        NSString *string = [NSString stringWithFormat:@"%@", [result objectForKey:@"rows"]];
                        NSString *stringWithoutbracketsend = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
                        NSString *stringWithoutbracketsfront = [stringWithoutbracketsend stringByReplacingOccurrencesOfString:@"(" withString:@""];
                        NSString *completion = [stringWithoutbracketsfront stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
                        NSString *newStr = [completion substringFromIndex:1];
                        NSString *finalstring = [newStr substringToIndex:newStr.length-(newStr.length>0)];

                        if (completion)
                            completion(finalstring, nil);

                        // or, you might want to explicitly dispatch that completion block back to the main queue:
                        //
                        // if (completion)
                        //     dispatch_async(dispatch_get_main_queue(), ^{
                        //         completion(finalstring, nil);
                        //     });
                    }
                }];
}

You'd then invoke it something like:

[self SQLRetrieve:@"your-bar-id" completion:^(NSString *string, NSError *error) {
    if (error) {
        // do whatever you want upon error
        NSLog(@"Error %@", error );
    } else {
        // do whatever you want with the string, for example
        [MyArray addObject:string];
    }
}];

I don't know how to reconcile the fact that you declared a method that claims to return an array but you appear to return a string, so I simply presumed you really intended the latter (as you can see from my code sample). If you really wanted to return the array, then just change that completion block to pass back an array, and change to code to actually pass back that array.

A related, but more subtle issue here is that you appear to be that your original code sample is returning a string, but also adding the string to MyArray, which you don't reference elsewhere in this code sample. You should be very wary about updating model objects from within an asynchronously called block of code. (Because while this code is running asynchronously, you might have a UI that is continuing to reference MyArray, and it might be confused if you change it mid-stream.) It's probably safer to have the block that you pass to SQLRetrieve update MyArray, like I have above. And if invokeAPI's completion block is not guaranteed to run on the main queue, you might want to explicitly dispatch your own completion block back to the main queue. You want to make sure that you not only update the UI on the main queue, but you probably also want to make sure you update your model objects in the main queue only, too (it's the easiest way to avoid synchronization errors).

Rob
  • 415,655
  • 72
  • 787
  • 1,044
1

The return type of the expected block type is void, not NSString as you have specified by adding a return. You can't return a value.

Peter DeWeese
  • 18,141
  • 8
  • 79
  • 101
0

First off where would you store the return? Commonly returns are assigned to variables in the call:

NSString *string = [NSString alloc] init];

The return of [[NSString alloc] init] is assigned to *string. In your case:

[self.client invokeAPI ...

There is no variable for the return to be assigned to, so your return is pointless.

Now for the error: It says you are trying to send a function with return type NSString* to a function of return type void (MSAPIBlock aka (void(^)). Async blocks cannot return a variable to your code because they can finish after your code does. In your case I think you need to dispatch into a separate function to deal with your finalString. At the end of your completion block add:

dispatch_async( dispatch_get_main_queue(), ^{
    [self doSomethingWithReturnString:finalString];
});

EDIT: added complete code for your function

- (NSArray *)SQLRetrieve:(NSString *)barID {

    self.client = [MSClient clientWithApplicationURLString:@"https://outnight-mobile.azure-mobile.net/" applicationKey:@"okYeRGfBagYrsbkaqWIRObeDtktjkF10"];
    [self.client invokeAPI:@"photos"
                      body:barID
                HTTPMethod:@"POST"
                parameters:nil
                   headers:nil
                completion:^(id result, NSHTTPURLResponse *response, NSError *error) {
                    if (error) {
                        NSLog(@"Error %@", error );
                    } else
                    {
                        NSString *string = [NSString stringWithFormat:@"%@", [result objectForKey:@"rows"]];
                        NSString *stringWithoutbracketsend = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
                        NSString *stringWithoutbracketsfront = [stringWithoutbracketsend stringByReplacingOccurrencesOfString:@"(" withString:@""];
                        NSString *completion = [stringWithoutbracketsfront stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
                        NSString *newStr = [completion substringFromIndex:1];
                        NSString *finalstring = [newStr substringToIndex:newStr.length-(newStr.length>0)];
                        //NSLog(@"%@", finalstring);
                        [MyArray addObject:finalstring];
                        NSLog(@"%@", [MyArray objectAtIndex:0]);

                        dispatch_async( dispatch_get_main_queue(), ^{
                            [self doSomethingWithReturnString:finalString];
                        });
                    }
                }];
}

-(void)doSomethingWithReturnString:(NSString*)string
{
    //Do whatever you need to with the return string here, 
    //if you don't need it anymore then no need to call this function 
    //(you already added it to your array)
}
Putz1103
  • 6,211
  • 1
  • 18
  • 25
  • Sounds fair, can you help a little further please as I am completely stuck – user3229170 Feb 12 '14 at 14:20
  • I gave you code, please tell me what you are confused on and I will try my best. – Putz1103 Feb 12 '14 at 14:21
  • ok, so i need to put the final string into another method and store them in there. – user3229170 Feb 12 '14 at 14:22
  • so firstly I assign the method as VOID and return no variables, then assign the variable with alloc init. then put the dispatch_async at the bottom of the block, then get another method that stores these variables ? – user3229170 Feb 12 '14 at 14:23
  • You don't need to assign the method to anything, just don't call return. Then you can dispatch and use the string if you need to. If all you want is the string added to your array then you don't need the dispatch, just continue on with your life. – Putz1103 Feb 12 '14 at 14:27