【问题标题】:Inconsistent behavior of openParentApplication in my WatchKit App我的 WatchKit 应用程序中 openParentApplication 的行为不一致
【发布时间】:2015-07-03 15:23:14
【问题描述】:

我正在开发一个 Apple Watch 应用程序,它使用 openParentApplication:reply: 方法与其父应用程序进行通信。

父应用与 Web 服务通信,并通过调用 reply 方法和包含数据的 NSDictionary 将其获取的数据发送回手表扩展。

当父应用在前台或后台打开时,该应用可以完美运行。但是如果我打开父应用程序然后使用任务切换器终止它,手表扩展程序第一次调用openParentApplication:replyInfo:,它会收到以下错误并且参数replyInfo 为nil。

UIApplicationDelegate in the iPhone App never called reply()

但是在此之后,扩展程序发出的每个openParentApplication:replyInfo: 呼叫都会得到正确的响应。

我检查并发现手表扩展第一次拨打电话时,handleWatchKitExtensionRequest:reply: 永远不会在父应用上被调用。

这可能是什么原因?

按照文档中的建议,我正在后台任务中执行 handleWatchKitExtensionRequest:reply: 中的所有操作。这是我的一些代码: 我的扩展代码:

NSDictionary *params = @{@"requestCode": @(RequestGetLoggedIn)};

[WKInterfaceController openParentApplication:params reply:^(NSDictionary *replyInfo, NSError *error) {
    // Do something with the result
}];

来自父应用的代码:

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *))reply
{
    self.backgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
    }];

    NSNumber* requestCode = userInfo[@"requestCode"];

    // Perform some request and then call reply()

    // End the background task
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTask];
    });
}

编辑 1:问题出现在模拟器和真正的 Apple Watch 上。

【问题讨论】:

  • 或许my answer here可以帮到你
  • 我认为情况并非如此,因为在第一次通话后一切正常。如果application:didFinishLaunchingWithOptions: 出现问题,则应用程序永远不会在后台打开,因此调用将永远不会得到任何响应。
  • 回应编辑#2:为什么在方法结束时启动后台任务会有帮助?您需要立即启动后台任务并在其中执行您的工作。我认为这不是 iOS 中的错误。听起来您的应用在有机会回复之前就被杀死了(由于缺少后台任务)。
  • Edit 2 是对以下答案的回应,是的,正如预期的那样,它没有改变任何东西,所以我刚刚删除了它。关于您的评论,在我上面的代码中,我已经立即开始任务,然后再执行其他任何操作。此外,如果问题在后台被杀死,那么我的电话总是会失败,不仅仅是第一次。我在下面的答案中的日志强烈表明这是由于操作系统的不稳定行为造成的。如果仅在扩展程序出现错误后启动父应用程序,则无法发回响应。

标签: ios watchkit apple-watch


【解决方案1】:

iOS 8.4 中似乎存在错误。

我在application:didFinishLaunchingWithOptions:handleWatchKitExtensionRequest:reply: 的开头添加了NSLog,执行了导致问题的操作,然后检查了设备日志并得到了这个:

--- Notice>: (Warn ) WatchKit: <SPCompanionAppServer.m __91-[SPCompanionAppServer launchCompanionAppForGizmoAppWithIdentifier:withUserInfoData:reply:]_block_invoke_2:1450> Got BSActionErrorCodeResponseNotPossible for com.xyz.xyz.watchkitapp. This will translate to WatchKitApplicationDelegateWatchKitRequestReplyNotCalledError

... Irrelevant stuff

--- WatchKit Extension[1686] <Warning>: __59-[InformationController getNotificationListIncremental:]_block_invoke (null)
**--- <Warning>: MY LOG: Application did launch with parameters (null)**

此日志显示application:didFinishLaunchingWithOptions: 在操作系统给出关于未从父应用程序获得响应的错误后被调用。如果没有先启动,应用程序将如何响应?

出现此问题时再次调用openParentApplication:reply:方法暂时解决了问题。

我实现重试一次行为的方法是创建一个包装调用的方法并使用该方法而不是原始方法。我将此作为类方法添加到实用程序类中,但它也可以是全局函数。

+ (void)openParentApplication:(NSDictionary*)params reply:(void(^)(NSDictionary *replyInfo, NSError *error))reply
{
    [WKInterfaceController openParentApplication:params reply:^(NSDictionary *replyInfo, NSError *error) {
        if (error.domain == WatchKitErrorDomain && error.code == WatchKitApplicationDelegateWatchKitRequestReplyNotCalledError)
        {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [WKInterfaceController openParentApplication:params reply:^(NSDictionary *replyInfo, NSError *error) {
                    reply(replyInfo, error);
                }];
            });
        }
        else
            reply(replyInfo, error);
    }];
}

【讨论】:

  • 这似乎是从 iOS8.4 开始的,虽然这个重试建议似乎有效,但我最好完全摆脱 openParentApplication 以支持在 watchkit 扩展中完成工作。我发现 WWDC “Watchkit Tips and Tricks”视频在这方面很有帮助。由于在 WatchOS2 中不推荐使用 openParentApplication,因此我的方法应该会带来好处。
  • 我想我在 8.3 中也经历过同样的行为。 WatchKit 连接在 iOS 9.0 之前不可用,因此除非父应用不支持任何早期版本的 iOS,否则我们将无法使用 WatchOS2,因此必须依赖 openParentApplication。
  • 代替 if (replyInfo == nil),您可以使用 (error.domain == WatchKitErrorDomain AND error.code == WatchKitApplicationDelegateWatchKitRequestReplyNotCalledError)
  • @Cihan,您可以在手表扩展中完成这项工作,但需要进行更广泛的重新设计(例如,使用文件/钥匙串访问组、框架库等)。无论如何,值得一看 WWDC 视频,无论如何,你最终都会被 Apple 强迫离开 openParentApplication。感谢您的帖子!
  • @CSmith,我看过你所指的会话,但我认为它们不能完全取代 openParentApplication 在 WatchOS 1 中所做的事情。我同意我们最终会离开 openParentApplication,但不幸的是,在放弃对 iOS 8.x 的支持之前。无论如何,任何替代方法都必须由 Watchkit Connectivity 代替。 Apple WatchOS 1 确实做得很差。这是一个半生不熟的产品。 WatchOS 2 应该是 WatchOS 1。通过在手表上做所有事情,它将提供无限更好的用户体验。
【解决方案2】:

我遇到了同样的问题。请参阅我为向 Apple 报告错误而制作的以下基本项目:https://www.dropbox.com/s/ayltpprjck37ins/HandleWatchkitExtensionError%202.zip?dl=0

@Cihan Tek - 你怎么又打电话给 openParentApplication:reply: 了?您是在回复块中调用它吗?执行此操作时,我仍然收到错误消息。

【讨论】:

  • 感谢@CihanTek。我注意到,如果您在 applicationDidFinishLaunching 完成之前尝试调用 openParentApplication:reply:,您仍然会收到错误消息。我不得不稍微延迟我的第二次通话,以确保一切都已初始化。
猜你喜欢
  • 2016-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
相关资源
最近更新 更多