0

I have created a block void function wich is using AFNetworking library. This function is used for GET request and uses like so.

[Mics getRequestBLOCK:clientsUrlStr BlockRequest:^(NSString *response) {
    //use response here
}];

Everything work fine, but I really get a headache when I use this block in a function that need return a value, for instance.

+ (NSArray*)OnlineClients
{
    NSString *clientsUrlStr = [NSString stringWithFormat:@"%@/sinch/clients", ROOT_URL];
    __block NSArray *clients = [[NSArray alloc] init];
    [Mics getRequestBLOCK:clientsUrlStr BlockRequest:^(NSString *response) {
        clients = [response componentsSeparatedByString: @","];
    }];

    return clients;
}

OnlineClients array function always return 0 item because request block is not finished with completion.

So, how can I wait for block completion to get response request for return in a function?

I have been searching various topics in StackOverflow and google but can't found any solution.

Please help.

Viet Nguyen
  • 2,285
  • 2
  • 26
  • 43
  • 1
    Then you haven't searched very well. This is probably the most commonly asked and answered question in all of iOS programming on Stack Overflow. – matt Sep 08 '16 at 02:45
  • Possible duplicate of [iOS: Waiting for API Completion Block and returning the result](http://stackoverflow.com/questions/23507331/ios-waiting-for-api-completion-block-and-returning-the-result) – HAS Sep 08 '16 at 07:05
  • http://stackoverflow.com/a/23507361/1489885 – HAS Sep 08 '16 at 07:07

2 Answers2

3

There are several possibilities to deal with your problem. You are able to either use dispatch_group or dispatch_semaphore to wait for the response from async call before return value or modify your method to have a block to callback the response. Please take a look on codes below. To be NOTICED that dispatch_group and dispatch_semaphore are going to block the current thread to wait until another one triggers a signal, therefore, should not use it on the main thread. In my opinion, I would suggest the 3rd solution. I hope it would help you.

//1
+ (NSArray*)OnlineClients
{
    dispatch_group_t serviceGroup = dispatch_group_create();

    NSString *clientsUrlStr = [NSString stringWithFormat:@"%@/sinch/clients", ROOT_URL];
    __block NSArray *clients = [[NSArray alloc] init];
    [Mics getRequestBLOCK:clientsUrlStr BlockRequest:^(NSString *response) {
        clients = [response componentsSeparatedByString: @","];
        dispatch_group_leave(serviceGroup);
    }];

    dispatch_group_enter(serviceGroup);

    return clients;
}

//2
+ (NSArray*)OnlineClients
{
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    NSString *clientsUrlStr = [NSString stringWithFormat:@"%@/sinch/clients", ROOT_URL];
    __block NSArray *clients = [[NSArray alloc] init];
    [Mics getRequestBLOCK:clientsUrlStr BlockRequest:^(NSString *response) {
        clients = [response componentsSeparatedByString: @","];
        dispatch_semaphore_signal(sema);
    }];

    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

    return clients;
}

//3
+ (void)getOnlineClientsWithCompletionBlock:(void (^) (NSArray *clients))completionBlock
{   
    NSString *clientsUrlStr = [NSString stringWithFormat:@"%@/sinch/clients", ROOT_URL];
    __block NSArray *clients = [[NSArray alloc] init];
    [Mics getRequestBLOCK:clientsUrlStr BlockRequest:^(NSString *response) {
        clients = [response componentsSeparatedByString: @","];
        if (completionBlock) {
            completionBlock(clients);
        }
    }];

}
HSG
  • 1,224
  • 11
  • 17
  • 1 & 2 are going to block the thread right, which if on the main thread is going to block any UI interactions. Might be useful to mention that. – sbarow Sep 08 '16 at 02:14
  • @sbarow exactly. Thank you for your point, should be careful in term of thread blocking. – HSG Sep 08 '16 at 02:17
  • First, thanks for help me. Solution 1: I don't see this waiting for anything, array still return `0 elements`, honestly I have tried this before but it does not work. Solution 2: When I apply this code like I have done this before, this is waiting forever and never goes to return. – Viet Nguyen Sep 08 '16 at 02:30
  • @vietnguyen09 could you get rid of the main thread when trying to do it? – HSG Sep 08 '16 at 02:34
  • @HDT Yes for the first one, but none for the second one. – Viet Nguyen Sep 08 '16 at 02:35
  • 1
    @vietnguyen09 maybe I missed `dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER);` this at the next line of `dispatch_group_enter(serviceGroup);`, I used them and it worked from my side, why don't you refer the 3rd one which is really simple? – HSG Sep 08 '16 at 02:39
  • 1
    Option 1 is wrong as written. You should call `dispatch_group_enter` before calling `getRequestBLOCK` and you should call `dispatch_group_wait` where you now have `dispatch_group_enter`. – rmaddy Sep 08 '16 at 02:45
  • @HDT My project is using `ASIHTTPRequest` before and I have created a get request function that used in the whole project, but then `ASIHTTPRequest` can't work well when my API server using `SSL Client certificate authentication` so I have to use AFNetworking. And now, I trying to make that request function work in my whole project without change too many places :( – Viet Nguyen Sep 08 '16 at 02:45
0

An UGLY solution but it's works!

__block BOOL isChecked = NO;
YourService *yourService = [[YourService alloc] init];
[yourService checkStatus:string completionHandler:^(BOOL isSuccessful, Error *err) {
    isValid = err ? NO : isSuccessful;
    isChecked = YES;
}];
while (!isChecked) {
    [NSThread 0.6];
}
oskarko
  • 3,382
  • 1
  • 26
  • 26