Search code examples
iosobjective-cwatchkitapple-watch

WatchKit return reply() inside a block in handleWatchKitExtensionRequest:


I saw this SO post where apparently data was being fetched and returned to the Watch extension like so:

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void ( ^)( NSDictionary * ))reply
{
  if ( [[userInfo objectForKey:@"request"] isEqualToString:@"getData"] )
 {
  // get data
  // ...
  reply( data );
 }
}

But when I try to call 'reply()' inside a block after getting network data like so:

__block UIBackgroundTaskIdentifier watchKitHandler;

watchKitHandler = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"backgroundTask"
                                                               expirationHandler:^{
                                                                   watchKitHandler = UIBackgroundTaskInvalid;
                                                               }];   
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void ( ^)( NSDictionary * ))reply
{ 

NSMutableDictionary *response = [NSMutableDictionary dictionary];
[ClassObject getDataWithBlock:^(BOOL succeeded, NSError *error){
        
        if (succeeded)
        {
            [response setObject:@"update succeded" forKey:@"updateKey"];
            reply(response);
            
        }
        else
        {
            if (error)
            {
                [response setObject:[NSString stringWithFormat:@"update failed: %@", error.description] forKey:@"updateKey"]; 
                reply(response);
            }
            else
            {
                [response setObject:@"update failed with no error" forKey:@"updateKey"];
                reply(response);
            }
        }
    }];
}

dispatch_after(dispatch_time( DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC * 180), dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [[UIApplication sharedApplication] endBackgroundTask:watchKitHandler];
});

I get this error:

"The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]"

So I guess I have to call reply() immediately and the only way to send fresh network data after WatchKit wakes up the parent app is to use MMWormhole?


Solution

  • To make sure that your asynchronous data fetch does not return immediately (without calling reply), you may try the following:

    - (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void ( ^)( NSDictionary * ))reply
    { 
        __block UIBackgroundTaskIdentifier watchKitHandler;
    
        watchKitHandler = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"backgroundTask"
                                                                   expirationHandler:^{
                                                                       watchKitHandler = UIBackgroundTaskInvalid;
                                                                   }];  
    
       NSMutableDictionary *response = [NSMutableDictionary dictionary];
    
       dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    
       [ClassObject getDataWithBlock:^(BOOL succeeded, NSError *error){
    
            if (succeeded)
            {
                [response setObject:@"update succeded" forKey:@"updateKey"];
                reply(response);
    
            }
            else
            {
                if (error)
                {
                    [response setObject:[NSString stringWithFormat:@"update failed: %@", error.description] forKey:@"updateKey"]; 
    
                    dispatch_semaphore_signal(sema);
    
                    reply(response);
                }
                else
                {
                    [response setObject:@"update failed with no error" forKey:@"updateKey"];
    
                    dispatch_semaphore_signal(sema);
    
                    reply(response);
                }
            }
        }];
    
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    
        dispatch_after(dispatch_time( DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC * 1), dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [[UIApplication sharedApplication] endBackgroundTask:watchKitHandler];
      });
    }