iosobjective-cwatchkitwatchos-3watchos-4

In apple iWatch background task is not getting called or triggering with schedule time


Update location when a task is triggered when the app in background. But unable to execute a task with background mode. My sample code with scheduleBackgroundRefreshWithPreferredDate is as below

[WKExtension.sharedExtension scheduleBackgroundRefreshWithPreferredDate:[NSDate dateWithTimeIntervalSinceNow:60] userInfo:nil scheduledCompletion:^(NSError * _Nullable error) {

        if(error == nil) {
            NSLog(@"background refresh task re-scheduling successfuly  ");

        } else{

            NSLog(@"Error occurred while re-scheduling background refresh: %@",error.localizedDescription);
        }
    }];

After schedule task reschedule in handleBackgroundTasks:

- (void)handleBackgroundTasks:(NSSet<WKRefreshBackgroundTask *> *)backgroundTasks
{
    for (WKRefreshBackgroundTask * task in backgroundTasks) {

        if ([task isKindOfClass:[WKApplicationRefreshBackgroundTask class]]) {
            WKApplicationRefreshBackgroundTask *backgroundTask = (WKApplicationRefreshBackgroundTask*)task;
            // location update methods schedule as background task
            [self startLocationUpdate];
            [backgroundTask setTaskCompleted];

        } else if ([task isKindOfClass:[WKSnapshotRefreshBackgroundTask class]]) {
            WKSnapshotRefreshBackgroundTask *snapshotTask = (WKSnapshotRefreshBackgroundTask*)task;
            [snapshotTask setTaskCompletedWithDefaultStateRestored:YES estimatedSnapshotExpiration:[NSDate distantFuture] userInfo:nil];

        } else if ([task isKindOfClass:[WKWatchConnectivityRefreshBackgroundTask class]]) {
            WKWatchConnectivityRefreshBackgroundTask *backgroundTask = (WKWatchConnectivityRefreshBackgroundTask*)task;
            [backgroundTask setTaskCompleted];

        } else if ([task isKindOfClass:[WKURLSessionRefreshBackgroundTask class]]) {
            WKURLSessionRefreshBackgroundTask *backgroundTask = (WKURLSessionRefreshBackgroundTask*)task;
            [backgroundTask setTaskCompleted];

        } else {
            [task setTaskCompleted];
        }
    }
}

Background task methods as below

-(void)startLocationUpdate {

    locationMgr = [[CLLocationManager alloc] init];
    [locationMgr setDelegate:self];

    locationMgr.desiredAccuracy = kCLLocationAccuracyBest;
    locationMgr.distanceFilter = kCLDistanceFilterNone;

    //    locationMgr.allowsBackgroundLocationUpdates = YES;

    [locationMgr requestAlwaysAuthorization];
    [locationMgr startUpdatingLocation];

    [WKExtension.sharedExtension scheduleBackgroundRefreshWithPreferredDate:[NSDate dateWithTimeIntervalSinceNow:60] userInfo:nil scheduledCompletion:^(NSError * _Nullable error) {

        if(error == nil) {
            NSLog(@"background refresh task re-scheduling successfuly  ");

        } else{

            NSLog(@"Error occurred while re-scheduling background refresh: %@",error.localizedDescription);
        }
    }];

}
- (void)locationManager:(CLLocationManager *)manager
     didUpdateLocations:(NSArray<CLLocation *> *)locations {

    NSTimeInterval locationAge = -[[locations lastObject].timestamp timeIntervalSinceNow];

    NSLog(@"Location Age : %f",locationAge);

    if (locationAge > 5.0) return;

    NSLog(@"latitude: %f longitude: %f",[locations lastObject].coordinate.latitude,[locations lastObject].coordinate.longitude);

    //NSString *strLocation = [NSString stringWithFormat:@"%f,%f" ,[locations lastObject].coordinate.latitude , [locations lastObject].coordinate.longitude];
    NSString *strLocation = @"bgLocation";

    NSDictionary *applicationData = [[NSDictionary alloc] initWithObjects:@[strLocation] forKeys:@[@"watchlocation"]];

   [[WCSession defaultSession] transferUserInfo:applicationData];

}

Solution

  • Background execution is very difficult on watchOS3. There are many restrictions, and even if you've successfully scheduled background refresh task, there is no guarantee that watchOS will start it.

    According to my experience after digging into WWDC sessions and documentation:

    1. watchOS3 will not give your app any background execution time if app is not in Dock or do not have active complication on current watchface
    2. number of background refresh tasks is limited to about 1 per hour for apps in Dock and 2 per hour for apps with active complication
    3. time of background execution is also limited, and if app exceeds this time, it will be terminated by watchOS daemon
    4. once you call setTaskCompleted, app goes to suspended state, so async locationManager:didUpdateLocations: method from your code should not be executed