배경에서 코드 WCSessionDelegate
을 handleBackgroundTasks:
을 통해 WKWatchConnectivityRefreshBackgroundTask
을 수락하기 위해 포 그라운드로 사용하는 것을 피하려고합니다. 설명서는 백그라운드 작업이 비동기 적으로 이루어질 수 있으며 WCSession
의 hasContentPending
이 NO
이 될 때까지 setTaskCompleted
으로 호출해서는 안됩니다.WKessionDelegate와 경쟁하는 WKWatchConnectivityRefreshBackgroundTask
내 시계 앱을 배경에 넣고 iPhone 앱에서 transferUserInfo:
을 받으면 내 처음으로 WKWatchConnectivityRefreshBackgroundTask
을받을 수 있습니다. 그러나 hasContentPending
은 항상 YES
이므로 작업을 저장하지 않고 단순히 내 WCSessionDelegate
메서드에서 반환합니다. 다시 transferUserInfo:
인 경우 hasContentPending
은 NO
이지만이 메시지와 관련된 WKWatchConnectivityRefreshBackgroundTask
은 없습니다. 즉, 이후 transferUserInfo:
은 handleBackgroundTask:
에 대한 호출을 트리거하지 않으며 단순히 WCSessionDelegate
에 의해 처리됩니다. hasContentPending
을 확인하지 않고 즉시 setTaskCompleted
이라고해도 transferUserInfo:
은 session:didReceiveUserInfo:
에 의해 처리되고 WCSession
을 다시 활성화 할 필요가 없습니다.
나는 무엇을해야할지 잘 모르겠다. WCSession
을 강제로 비활성화하는 방법이없는 것 같습니다. 그리고 지연에 대한 문서를 따르면 setTaskCompleted
은 OS 문제로 이어질 수 있습니다.
GitHub에 내 워크 플로를 보여주는 샘플 프로젝트를 게시하고 문서화하여 아래 WKExtensionDelegate
코드를 붙여 넣었습니다. 잘못된 선택을하거나 문서의 어딘가에서 잘못 해석하고 있습니까?
스위프트 3 버그 (rdar : // 28503030)를 수정 한 후 QuickSwitch 2.0 소스 코드를 살펴본 결과, 해당 메소드가 작동하지 않는 것 같습니다 (이에 대해서는 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
흥미 롭습니다. 감사합니다. 필자는 디버거에 연결된 시뮬레이터에서만 테스트를 수행했기 때문에이를 믿을 수 있습니다. 이것이 사실이라면, KVO는 올바른 동작처럼 들립니다 ('BackgroundTask'를 제외하고'hasContentPending' 옵저버가 'NO'로 트리거 될 때 클리어합니다). 내가 답으로 표시하기 전에 다시 한 번 테스트 해 보겠습니다. – greg
네, 시뮬레이터가 디버거가 부착 된 장치보다 예상 동작에 덜 부정확하다는 것을 알았습니다. 시뮬레이터에서 프로세스가 실행되면 다시는 중단되지 않으므로 설명하는 내용과 일치합니다. – ccjensen