2016-09-27 10 views
0

배경에서 코드 WCSessionDelegatehandleBackgroundTasks:을 통해 WKWatchConnectivityRefreshBackgroundTask을 수락하기 위해 포 그라운드로 사용하는 것을 피하려고합니다. 설명서는 백그라운드 작업이 비동기 적으로 이루어질 수 있으며 WCSessionhasContentPendingNO이 될 때까지 setTaskCompleted으로 호출해서는 안됩니다.WKessionDelegate와 경쟁하는 WKWatchConnectivityRefreshBackgroundTask

내 시계 앱을 배경에 넣고 iPhone 앱에서 transferUserInfo:을 받으면 내 처음으로 WKWatchConnectivityRefreshBackgroundTask을받을 수 있습니다. 그러나 hasContentPending은 항상 YES이므로 작업을 저장하지 않고 단순히 내 WCSessionDelegate 메서드에서 반환합니다. 다시 transferUserInfo: 인 경우 hasContentPendingNO이지만이 메시지와 관련된 WKWatchConnectivityRefreshBackgroundTask은 없습니다. 즉, 이후 transferUserInfo:handleBackgroundTask:에 대한 호출을 트리거하지 않으며 단순히 WCSessionDelegate에 의해 처리됩니다. hasContentPending을 확인하지 않고 즉시 setTaskCompleted이라고해도 transferUserInfo:session:didReceiveUserInfo:에 의해 처리되고 WCSession을 다시 활성화 할 필요가 없습니다.

나는 무엇을해야할지 잘 모르겠다. WCSession을 강제로 비활성화하는 방법이없는 것 같습니다. 그리고 지연에 대한 문서를 따르면 setTaskCompleted은 OS 문제로 이어질 수 있습니다.

GitHub에 내 워크 플로를 보여주는 샘플 프로젝트를 게시하고 문서화하여 아래 WKExtensionDelegate 코드를 붙여 넣었습니다. 잘못된 선택을하거나 문서의 어딘가에서 잘못 해석하고 있습니까?

스위프트 3 버그 (rdar : // 28503030)를 수정 한 후 QuickSwitch 2.0 소스 코드를 살펴본 결과, 해당 메소드가 작동하지 않는 것 같습니다 (이에 대해서는 another SO thread입니다). 나는 WCSessionhasContentPendingactivationState에 대해 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 

답변

1

귀하의 설명과 제 이해에서 올바르게 작동하는 것 같습니다.

이 나에게 설명 된 방법은 watchOS에 새로운 handleBackgroundTasks:이 방법을하기위한 것입니다 것입니다 :

  • 이 시작되는 이유 WatchKit 확장에 통신 할 수있는 시스템/배경에 재개 ,
  • WatchKit 확장 프로그램은 원하는 작업을 완료했을 때 시스템에 알리고 다시 종료/일시 중단 할 수있는 방법을 제공합니다.

이 즉, 들어오는 WatchConnectivity 페이로드는 시계에 수신하고 WatchKit 확장이 종료 또는 당신이 백그라운드에서 실행하는 이유를 알려주는 하나의 handleBackgroundTasks: 콜백을 기대한다 일시 중단 될 때마다. 즉, 1 WKWatchConnectivityRefreshBackgroundTask을받을 수 있지만 여러 WatchConnectivity 콜백 (파일, userInfos, applicationContext)을받을 수 있습니다. hasContentPendingWCSession이 보류중인 모든 초기 컨텐트 (파일, userInfos, applicationContext)를 전달한시기를 알려줍니다. 이 시점에서 WKWatchConnectivityRefreshBackgroundTask 개체의 setTaskCompleted를 호출해야합니다.

그런 다음 다른 handleBackgroundTasks: 콜백을받지 못하면 곧 워치Kit 확장이 일시 중단되거나 종료 될 것으로 예상되므로 완료 할 다른 WK 백그라운드 작업 객체가 있습니다.

OS가 디버거가 정상적으로 작동하지 않을 수있는 디버거가있는 프로세스에 연결할 때 이러한 종류의 문제가 발생하지 않도록하려면 로깅을 사용하여 여기에서 동작을 검사하는 것이 좋습니다. 문제의

+0

흥미 롭습니다. 감사합니다. 필자는 디버거에 연결된 시뮬레이터에서만 테스트를 수행했기 때문에이를 믿을 수 있습니다. 이것이 사실이라면, KVO는 올바른 동작처럼 들립니다 ('BackgroundTask'를 제외하고'hasContentPending' 옵저버가 'NO'로 트리거 될 때 클리어합니다). 내가 답으로 표시하기 전에 다시 한 번 테스트 해 보겠습니다. – greg

+1

네, 시뮬레이터가 디버거가 부착 된 장치보다 예상 동작에 덜 부정확하다는 것을 알았습니다. 시뮬레이터에서 프로세스가 실행되면 다시는 중단되지 않으므로 설명하는 내용과 일치합니다. – ccjensen