如何正确使用“openParentApplication”和“handleWatchKitExtensionRequest”以便调用“reply()”?

时间:2021-02-12 12:05:05

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.

情况:我在Watch应用程序中使用openParentApplication在主应用程序中调用handleWatchKitExtensionRequest。这在模拟器中运行良好,当iPhone应用程序处于活动/打开状态时,它也适用于实际设备(Apple Watch和iPhone)。

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.

问题:当我在实际设备(Apple Watch和iPhone)上运行它时,当主iPhone应用程序未处于活动/打开状态时,handleWatchKitExtensionRequest不会将数据返回到openParentApplication。

Code in InterfaceController.m in the WatchKit Extension:

WatchKit扩展中的InterfaceController.m中的代码:

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:

iPhone主应用程序的app代理中的代码:

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

1 个解决方案

#1


16  

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.

当iPhone上的主应用程序未处于活动状态时,可能无法访问reply(),因为之前操作系统已终止后台任务。

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.

解决方案是在文档中指定的handleWatchKitExtensionRequest中显式启动后台任务。如果后台任务是这样启动的,它最多可以运行180秒。这可以确保iPhone上的主应用程序在发送回复之前不会被暂停。

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

iPhone主应用程序的app代理中的代码:

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

#1


16  

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.

当iPhone上的主应用程序未处于活动状态时,可能无法访问reply(),因为之前操作系统已终止后台任务。

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.

解决方案是在文档中指定的handleWatchKitExtensionRequest中显式启动后台任务。如果后台任务是这样启动的,它最多可以运行180秒。这可以确保iPhone上的主应用程序在发送回复之前不会被暂停。

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

iPhone主应用程序的app代理中的代码:

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