1

My Apple Watch app sends a message to the companion iPhone app. In the main app's handleWatchKitExtensionRequest, I send a request to the server:

 - (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
    if ([[userInfo objectForKey:@"request"] isEqualToString:@"getPendingChallenge"]) {
        [MyClient getPendingNotifications:someId withDomain:host withSuccessBlock:^(id responseObject) {
            // process responseObject
            ...
            reply(response);
            return;
        } withFailureBlock:^(NSError *error, NSString *responseString) {
            // error handling
            return;
        }];
    }
}

getPendingNotifications above is just a regular network GET request using AFNetworking.

It all works well when the app is active. Because this network request is used to populate the UI on my Apple Watch, I do not wish the main app to be active. However, when the main app on iPhone is in background, I can see the network request being sent out, but the withSuccessBlock or withFailureBlock callback blocks in the above code never gets triggered.

Can the phone app receive network request responses in background mode? If so, what am I doing wrong?

SeaJelly
  • 1,738
  • 1
  • 15
  • 30
  • possible duplicate of [How do I correctly use "openParentApplication" and "handleWatchKitExtensionRequest" so that "reply()" is called?](http://stackoverflow.com/questions/30000274/how-do-i-correctly-use-openparentapplication-and-handlewatchkitextensionreque) – John May 31 '15 at 07:44
  • @vomako my reply() is being called fine, my issue is that when an asynchronous request to the server gets back to the phone the main app drops that callback. But for me the reply() on watch does get called because I always make sure I send back a reply() even if I'm just passing back nil. – SeaJelly May 31 '15 at 21:29

2 Answers2

2

I have found a solution online that works for me, a post (http://www.fiveminutewatchkit.com/blog/2015/3/11/one-weird-trick-to-fix-openparentapplicationreply) by Brian Gilham.

And here's the code that works for me.

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply {
    // There is a chance that the iOS app gets killed if it's in the background
    // before it has a chance to reply to Apple Watch.
    // The solution is to have the app respond to the request asap, then complete other tasks.
    // The following code begins – and ends, after two seconds – an empty background task right at the beginning of this delegate method
    // Then we kick off a background task for the real work
    // For more details see http://www.fiveminutewatchkit.com/blog/2015/3/11/one-weird-trick-to-fix-openparentapplicationreply

    __block UIBackgroundTaskIdentifier bogusWorkaroundTask;
    bogusWorkaroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:bogusWorkaroundTask];
    }];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] endBackgroundTask:bogusWorkaroundTask];
    });

    __block UIBackgroundTaskIdentifier realBackgroundTask;
    realBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        reply(nil);
        [[UIApplication sharedApplication] endBackgroundTask:realBackgroundTask];
    }];

    if ([[userInfo objectForKey:@"request"] isEqualToString:@"getPendingChallenge"]) {
        [self handleWatchKitGetPendingChallengeRequest:reply];
    }

    [[UIApplication sharedApplication] endBackgroundTask:realBackgroundTask];
}

- (void)handleWatchKitGetPendingChallengeRequest:(void (^)(NSDictionary *))reply {
    ...
    [MyClient getPendingNotifications:someId withDomain:host withSuccessBlock:^(id responseObject) {
        // process responseObject
        reply(response);
        return;
    } withFailureBlock:^(NSError *error, NSString *responseString) {
        // error handling
        reply(nil);
        return;
    }];
}
SeaJelly
  • 1,738
  • 1
  • 15
  • 30
0

Try to send the request as a synchronous request.

I guess that your request is asynchronous request (as it should be in regular cases). The problem that in background mode, the device will lunch your app in background thread, and you created a new thread for the request.

Yedidya Reiss
  • 5,316
  • 2
  • 17
  • 19
  • @SeaJelly That just was I thought... change it to synch one. – Yedidya Reiss May 30 '15 at 19:46
  • I don't want to pause my program to wait for the response, almost all requests in our clients are asynchronous – SeaJelly May 30 '15 at 19:48
  • Well, you should do it only (!) at the background state (you can check for this in the code). As i sad, all request should be asynchronous as you did. This is a special case since it is in background mode. – Yedidya Reiss May 30 '15 at 19:50