0

I'm trying to respond to location updates when my app is terminated. Apple's documentation states:

iOS supports the delivery of location events to apps that are suspended or no longer running.

It also states that startUpdatingLocation

Can use location updates in background mode YES

However, my didUpdateToLocation method is only called when the app is running in the foreground or in the background. When I terminate the app, it never wakes up to handle location updates. I've tested this on several simulated devices (iPhone 6, 7, 8, X) as well as a real iPhone 5s. I've also confirmed setAllowsBackgroundLocationUpdates is being called.

AppDelegate.m

#import "AppDelegate.h"
#import "RNFIRMessaging.h"
#import <CoreLocation/CoreLocation.h>

#import <React/RCTBridgeModule.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "NoteManager.h"

@implementation AppDelegate {
  UIApplication *_app;
  NoteManager *_noteManager;
  CLLocationManager* _locationManager;
}

- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSLog(@"--- Initializing application");
  _app = application;

  id<RCTBridgeDelegate> moduleInitialiser = self;
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:moduleInitialiser launchOptions:launchOptions];

  RCTRootView *rootView = [[RCTRootView alloc]
    initWithBridge:bridge
    moduleName:@"GoNote"
    initialProperties:nil
  ];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];

  [FIRApp configure];
  [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
  [self setupLocationManager];

  return YES;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
  // return [NSURL URLWithString:@"http://192.168.1.177:8081/index.ios.bundle?platform=ios"];
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
}

- (NSArray *)extraModulesForBridge:(RCTBridge *)bridge {
  NSLog(@"--- extraModulesForBridge");
  // Initialize our native modules here
  _noteManager = [[NoteManager alloc] initWithApp:_app];
  return @[
    _noteManager
  ];
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
  NSLog(@"--- pushNotifications willPresentNotification");
  [RNFIRMessaging willPresentNotification:notification withCompletionHandler:completionHandler];
}

#if defined(__IPHONE_11_0)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
  NSLog(@"--- pushNotifications didReceiveNotificationResponse");
  [RNFIRMessaging didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}
#else
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)())completionHandler {
  NSLog(@"--- pushNotifications didReceiveNotificationResponse");
  [RNFIRMessaging didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}
#endif

//You can skip this method if you don't want to use local notification
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
  NSLog(@"--- pushNotifications didReceiveLocalNotification");
  [RNFIRMessaging didReceiveLocalNotification:notification];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
  NSLog(@"--- pushNotifications didReceiveLocalNotification");
  [RNFIRMessaging didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

- (void) applicationDidEnterBackground:(UIApplication *)application {
  NSLog(@"--- applicationDidEnterBackground");
  [_locationManager stopUpdatingLocation];

  __block UIBackgroundTaskIdentifier bgTask = [_app beginBackgroundTaskWithExpirationHandler:^{
    bgTask = UIBackgroundTaskInvalid;
  }];

  [NSTimer
    scheduledTimerWithTimeInterval: 10.0
    target: self
    selector: @selector(startTrackingBg)
    userInfo: nil
    repeats: YES
  ];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError*)error {
  NSLog(@"--- (NoteManager) locationManager didFailWithError %@", error);
}

- (void) setupLocationManager {
  NSLog(@"--- setupLocationManager");
  CLLocationManager* locationManager = _locationManager = [[CLLocationManager alloc] init];
  if([locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
    NSLog(@"--- setAllowsBackgroundLocationUpdates:YES");
    [locationManager setAllowsBackgroundLocationUpdates:YES];
  } else {
    NSLog(@"--- setAllowsBackgroundLocationUpdates:NO");
  }
  [locationManager setDelegate:self];
  [locationManager requestAlwaysAuthorization];
  [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
  [locationManager setDistanceFilter:10];
  [locationManager setPausesLocationUpdatesAutomatically:NO];
  [locationManager startUpdatingLocation];
}

- (void) startTrackingBg {
#if !TARGET_OS_TV
  NSLog(@"--- startTrackingBg");
  [_locationManager startUpdatingLocation];
#endif
}

- (void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
  NSLog(@"--- didUpdateToLocation");
  if(_noteManager != nil) {
    [_noteManager updateLocation:newLocation];
  }
}

@end

AppDelegate.h

#import <UIKit/UIKit.h>
#import <React/RCTBridgeDelegate.h>
#import <CoreLocation/CoreLocation.h>
@import UserNotifications;

@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate, RCTBridgeDelegate, CLLocationManagerDelegate>

@property (nonatomic, strong) UIWindow *window;

@end
jscs
  • 63,694
  • 13
  • 151
  • 195
SimpleJ
  • 13,812
  • 13
  • 53
  • 93
  • 1
    Note that the delegate method `didUpdateToLocation` is deprecated. You should use `didUpdateLocations` – Paulw11 Oct 17 '17 at 19:36

1 Answers1

2

This doesn't work exactly how you want it. The location updates work when you monitor significant location changes using

startMonitoringSignificantLocationChanges()

Read this answer here where someone was trying almost the same thing - Restart location updates from the background

You can read the implementation here https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html

Prateek Varshney
  • 1,114
  • 2
  • 12
  • 29
  • Depending on the application purpose, you can also use CLVisit, or region monitoring. Both wake up terminated app in background and call the update location. – Damien Oct 18 '17 at 09:30