我試圖將我的代碼從前臺回調中僅使用WCSessionDelegate
回調改爲通過handleBackgroundTasks:
在後臺接受WKWatchConnectivityRefreshBackgroundTask
。該文檔指出,後臺任務可能異步進入,並且不應該調用setTaskCompleted
,直到WCSession
的hasContentPending
爲NO
。WKWatchConnectivityRefreshBackgroundTask與WCSessionDelegate競爭
如果我從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 bug後,rdar:// 28503030),他們的方法似乎不工作(關於這個,有another SO thread)。我已經嘗試使用KVO的WCSession
的hasContentPending
和activationState
,但仍然沒有任何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
有趣,謝謝。我承認只是在連接到調試器的模擬器上進行測試,所以我可以相信這一點。如果這是真的,那麼KVO聽起來像是正確的動作(保存任何'BackgroundTask's,並且當'hasContentPending'觀察者被觸發爲'NO'時清除它們)。在我回過頭來回答之前,讓我再做一些測試。 – greg
是的,我發現仿真器對於預期行爲的準確度要比附加調試器的設備要準確得多。我在模擬器中發現,一旦進程正在運行,它不會再次掛起,因此與您所描述的內容相符。 – ccjensen