【发布时间】:2016-09-27 23:14:36
【问题描述】:
我正在尝试调整我的代码,从仅在前台使用 WCSessionDelegate 回调到在后台通过 handleBackgroundTasks: 接受 WKWatchConnectivityRefreshBackgroundTask。该文档指出后台任务可能会异步进入,并且在WCSession 的hasContentPending 为NO 之前不应调用setTaskCompleted。
如果我将手表应用程序置于后台并从 iPhone 应用程序中输入transferUserInfo:,我将能够成功接收到我的第一个 WKWatchConnectivityRefreshBackgroundTask。然而,hasContentPending 始终是YES,所以我保存了任务并简单地从我的WCSessionDelegate 方法返回。如果我再次transferUserInfo:,hasContentPending 是NO,但没有与此消息关联的WKWatchConnectivityRefreshBackgroundTask。也就是说,后续的transferUserInfo: 不会触发对handleBackgroundTask: 的调用——它们只是由WCSessionDelegate 处理。即使我立即setTaskCompleted 而不检查hasContentPending,后续的transferUserInfo: 也由session:didReceiveUserInfo: 处理,而我无需再次激活WCSession。
我不确定在这里做什么。似乎没有办法强制 WCSession 停用,并且遵循有关延迟 setTaskCompleted 的文档似乎让我遇到了操作系统问题。
我在GitHub 上发布并记录了一个示例项目,说明了我的工作流程,并在下面粘贴了我的WKExtensionDelegate 代码。我是否在某个地方做出了错误的选择或错误地解释了文档?
我查看了QuickSwitch 2.0 源代码(在修复了 Swift 3 错误之后,rdar://28503030),他们的方法似乎根本不起作用(another SO thread 关于这个)。我已经尝试对WCSession 的hasContentPending 和activationState 使用KVO,但仍然没有任何WKWatchConnectivityRefreshBackgroundTask 可以完成,这对于我目前对该问题的解释是有意义的。
#import "ExtensionDelegate.h"
@interface ExtensionDelegate()
@property (nonatomic, strong) WCSession *session;
@property (nonatomic, strong) NSMutableArray<WKWatchConnectivityRefreshBackgroundTask *> *watchConnectivityTasks;
@end
@implementation ExtensionDelegate
#pragma mark - Actions
- (void)handleBackgroundTasks:(NSSet<WKRefreshBackgroundTask *> *)backgroundTasks
{
NSLog(@"Watch app woke up for background task");
for (WKRefreshBackgroundTask *task in backgroundTasks) {
if ([task isKindOfClass:[WKWatchConnectivityRefreshBackgroundTask class]]) {
[self handleBackgroundWatchConnectivityTask:(WKWatchConnectivityRefreshBackgroundTask *)task];
} else {
NSLog(@"Handling an unsupported type of background task");
[task setTaskCompleted];
}
}
}
- (void)handleBackgroundWatchConnectivityTask:(WKWatchConnectivityRefreshBackgroundTask *)task
{
NSLog(@"Handling WatchConnectivity background task");
if (self.watchConnectivityTasks == nil)
self.watchConnectivityTasks = [NSMutableArray new];
[self.watchConnectivityTasks addObject:task];
if (self.session.activationState != WCSessionActivationStateActivated)
[self.session activateSession];
}
#pragma mark - Properties
- (WCSession *)session
{
NSAssert([WCSession isSupported], @"WatchConnectivity is not supported");
if (_session != nil)
return (_session);
_session = [WCSession defaultSession];
_session.delegate = self;
return (_session);
}
#pragma mark - WCSessionDelegate
- (void)session:(WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(NSError *)error
{
switch(activationState) {
case WCSessionActivationStateActivated:
NSLog(@"WatchConnectivity session activation changed to \"activated\"");
break;
case WCSessionActivationStateInactive:
NSLog(@"WatchConnectivity session activation changed to \"inactive\"");
break;
case WCSessionActivationStateNotActivated:
NSLog(@"WatchConnectivity session activation changed to \"NOT activated\"");
break;
}
}
- (void)sessionWatchStateDidChange:(WCSession *)session
{
switch(session.activationState) {
case WCSessionActivationStateActivated:
NSLog(@"WatchConnectivity session activation changed to \"activated\"");
break;
case WCSessionActivationStateInactive:
NSLog(@"WatchConnectivity session activation changed to \"inactive\"");
break;
case WCSessionActivationStateNotActivated:
NSLog(@"WatchConnectivity session activation changed to \"NOT activated\"");
break;
}
}
- (void)session:(WCSession *)session didReceiveUserInfo:(NSDictionary<NSString *, id> *)userInfo
{
/*
* NOTE:
* Even if this method only sets the task to be completed, the default
* WatchConnectivity session delegate still picks up the message
* without another call to handleBackgroundTasks:
*/
NSLog(@"Received message with counter value = %@", userInfo[@"counter"]);
if (session.hasContentPending) {
NSLog(@"Task not completed. More content pending...");
} else {
NSLog(@"No pending content. Marking all tasks (%ld tasks) as complete.", (unsigned long)self.watchConnectivityTasks.count);
for (WKWatchConnectivityRefreshBackgroundTask *task in self.watchConnectivityTasks)
[task setTaskCompleted];
[self.watchConnectivityTasks removeAllObjects];
}
}
@end
【问题讨论】:
标签: ios watchkit watchos watchconnectivity