找到你要的答案

Q:How do I correctly use “openParentApplication” and “handleWatchKitExtensionRequest” so that “reply()” is called?

Q:如何正确使用“openparentapplication”和“handlewatchkitextensionrequest”使“reply()”之称?

Situation: I use openParentApplication in the Watch app to call handleWatchKitExtensionRequest in the main app. This works nicely in the simulator and it also works on the actual devices (Apple Watch and iPhone) when the iPhone app is active/open.

Problem: When I run it on the actual devices (Apple Watch and iPhone), handleWatchKitExtensionRequest does not return data to openParentApplication when the main iPhone app is not active/open.

Code in InterfaceController.m in the WatchKit Extension:

NSDictionary *requst = @{ @"request" : @"getData" };
[InterfaceController openParentApplication:requst
                                     reply:^( NSDictionary *replyInfo, NSError *error ) {
                                        // do something with the returned info
                                     }];

Code in the app delegate of the main app on iPhone:

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

情境:我用openparentapplication在手表的应用程序在主应用程序调用handlewatchkitextensionrequest。这很好地在模拟器,它也适用于实际设备(Apple Watch和iPhone)时,iPhone的应用程序是活跃/开放。

问题:当我运行在实际设备(苹果手表和iPhone),handlewatchkitextensionrequest不返回数据openparentapplication当主iPhone应用程序是不活跃、开放。

代码接口、控制器。我在watchkit延伸:

NSDictionary *requst = @{ @"request" : @"getData" };
[InterfaceController openParentApplication:requst
                                     reply:^( NSDictionary *replyInfo, NSError *error ) {
                                        // do something with the returned info
                                     }];

在iPhone上的应用程序的主要应用程序代码:

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

When the main app on the iPhone is not active, reply() may not be reached because the background task is killed by the OS before.

The solution is to explicitly start a background task in handleWatchKitExtensionRequest as specified in the documentation. If a background task is initiated like this, it can run up to 180 seconds. This ensures that the main app on the iPhone is not suspended before it can send its reply.

Code in the app delegate of the main app on iPhone:

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

   if ( [[userInfo objectForKey:@"request"] isEqualToString:@"getData"] )
   {
      // get data
      // ...
      reply( data );
   }

   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];
   } );
}

In case you need to asynchroneously fetch data, use the following approach to ensure that the method does not return immediately (without calling reply):

- (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"];
        }
        else
        {
            if (error)
            {
                [response setObject:[NSString stringWithFormat:@"update failed: %@", error.description] forKey:@"updateKey"]; 
            }
            else
            {
                [response setObject:@"update failed with no error" forKey:@"updateKey"];
            }
        }

        reply(response);
        dispatch_semaphore_signal(sema);
    }];

    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];
  });
}

当iPhone上的主要应用是不活跃的,reply()可能不是因为背景的任务是被操作系统之前到达。

解决的办法是明确启动一个后台任务handlewatchkitextensionrequest作为文档中指定的。如果像这样启动后台任务,它最多可以运行180秒。这确保iPhone上的主应用程序在发送其回复前不会被暂停。

在iPhone上的应用程序的主要应用程序代码:

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

   if ( [[userInfo objectForKey:@"request"] isEqualToString:@"getData"] )
   {
      // get data
      // ...
      reply( data );
   }

   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];
   } );
}

如果你需要asynchroneously获取数据,使用下面的方法来保证方法不立即返回(不要求回答):

- (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"];
        }
        else
        {
            if (error)
            {
                [response setObject:[NSString stringWithFormat:@"update failed: %@", error.description] forKey:@"updateKey"]; 
            }
            else
            {
                [response setObject:@"update failed with no error" forKey:@"updateKey"];
            }
        }

        reply(response);
        dispatch_semaphore_signal(sema);
    }];

    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];
  });
}
ios  objective-c  watchkit