Swift iOS: Closure will not run in background when Remote Notification received - ios

I am writing an iOS app that requires the device's GPS loction to be updated when a push notification is received.
I use a closure to get the current GPS location. This code runs perfectly when the app is in the foreground (Both "New remote notification" and "Got location" is printed to console), but when the app is in the background, only "New remote notification" (and no location error) is printed to console. As a result I don't have the GPS location of the device at this point, which is very important for my app's functionality.
Any help would be greatly appreciated.
Thanks.
I have in my Info.plist file for 'Required background modes';
App registers for location updates
App downloads content from the network
App downloads content in response to push notifications
My app also has access to location at all times, including the background (successfully tested at other points in the code): NSLocationAlwaysUsageDescription is in the Info.plist file
In my AppDelegate file:
func application(application: UIApplication!, didReceiveRemoteNotification userInfo:NSDictionary!) {
println("New remote notification")
var notification:NSDictionary = userInfo as NSDictionary
var loc: LocationManager?
loc = LocationManager()
loc!.fetchWithCompletion {location, error in
// fetch location or an error
if let myloc = location {
var lat = myloc.coordinate.latitude as Double
var lon = myloc.coordinate.longitude as Double
println("Got location")
} else if let err = error {
println(err.localizedDescription)
}
loc = nil
}
}

If app is not in foreground, make sure to ask for a little time to complete the request with beginBackgroundTaskWithExpirationHandler and then call endBackgroundTask when done.
For more information, see the Executing Finite Length Tasks in the Background Execution chapter of the App Programming Guide for iOS.

Related

iOS swift backgound location update when killed/suspended with minimum data loss

I've several questions to ask regarding the location updates in background in swift language.
Let me explain what I'm doing in the app. I'm developing an app which regularly monitors users location (as all of you do) and updates it to the server, so the users movement is tracked and saved for future reference by the user.
Questions
What is the difference between using startMonitoringSignificantLocationChanges Vs startUpdatingLocation?
1.1 If we use startUpdatingLocation does it impact on publishing the app to the App Store?
When the app is killed/suspended (Force closed by the user) it takes some time to restart the location manager from the AppDelegate which causes loss of location data for a period of time. Any possible solution to overcome this?
2.1 The difference of time for restarting is around 30 sec to nearly 1 min, which doesn't triggers the location update and hence the route is not perfect as shown in the image
Output of the App where due to restart the locations are not recieved and hence the route goes over the road.
Code for reference
import UIKit
import GoogleMaps
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
let DBName = "test"
var logFile: FileUtils?
var viewController:ViewController?
var count = 0
var appOpenCount = 0
let totalPath = GMSMutablePath()
var leaveCoordinates = 0
var previousLocation: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
var locationManager: CLLocationManager?
var significatLocationManager : CLLocationManager?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
GMSServices.provideAPIKey("*********************")
logFile = FileUtils(fileName: "\(DBName).txt")
logFile!.appendFile("\n\nlaunchOptions : \(launchOptions)")
let defaults = NSUserDefaults.standardUserDefaults()
count = defaults.integerForKey("Enabled")
appOpenCount = defaults.integerForKey("appOpenCount")
if(UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Available){
logFile!.appendFile("\nYessss")
} else {
logFile!.appendFile("\nNooo")
}
appOpenCount += 1
defaults.setValue(appOpenCount, forKey: "appOpenCount")
defaults.synchronize()
if count == 0 {
count += 1
defaults.setValue(count, forKey: "Enabled")
defaults.synchronize()
Util.copyFile(count)
}
if let launchOpt = launchOptions{
if (launchOpt[UIApplicationLaunchOptionsLocationKey] != nil) {
logFile!.appendFile("\nExecuted on : significatLocationManager")
self.significatLocationManager = CLLocationManager()
self.significatLocationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.significatLocationManager?.delegate = self
self.significatLocationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.significatLocationManager!.allowsBackgroundLocationUpdates = true
}
self.significatLocationManager?.startUpdatingLocation()
}else{
logFile!.appendFile("\nExecuted on : locationManager1")
self.locationManager = CLLocationManager()
self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager?.delegate = self
self.locationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.locationManager!.allowsBackgroundLocationUpdates = true
}
self.locationManager?.startUpdatingLocation()
}
} else {
logFile!.appendFile("\nExecuted on : locationManager2")
self.locationManager = CLLocationManager()
self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager?.delegate = self
self.locationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.locationManager!.allowsBackgroundLocationUpdates = true
}
self.locationManager?.startUpdatingLocation()
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let locationArray = locations as NSArray
let newLocation = locationArray.lastObject as! CLLocation
let coordinate = newLocation.coordinate
let tempCoor = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
let lat = tempCoor.latitude
let lon = tempCoor.longitude
insert(lat, lon: lon)
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
if self.significatLocationManager != nil {
self.significatLocationManager?.startMonitoringSignificantLocationChanges()
}else{
self.locationManager?.startMonitoringSignificantLocationChanges()
}
logFile!.appendFile("\napplicationDidEnterBackground")
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func insert(lat: Double, lon: Double){
let locationInfo: LocationDetails = LocationDetails()
locationInfo.latitude = lat
locationInfo.longitude = lon
locationInfo.time = NSDate()
let db = "\(DBName)\(count).sqlite"
ModelManager.getInstance(db).addLocationData(locationInfo)
}
}
question-1
What is the difference between using startMonitoringSignificantLocationChanges Vs startUpdatingLocation?
startUpdatingLocation updates the location when it is called first time and then when the distance filter value exceeds.
it uses the GPS when its available, if you use in continuously it Drain your power/battery
Discussion
This method returns immediately. Calling this method causes the
location manager to obtain an initial location fix (which may take
several seconds) and notify your delegate by calling its
locationManager:didUpdateLocations: method. After that, the receiver generates update events primarily when the value in the distanceFilter property is exceeded. Updates may be delivered in other situations though. For example, the receiver may send another notification if the hardware gathers a more accurate location reading.
Calling this method several times in succession does not automatically
result in new events being generated. Calling stopUpdatingLocation in
between, however, does cause a new initial event to be sent the next
time you call this method.
If you start this service and your application is suspended, the
system stops the delivery of events until your application starts
running again (either in the foreground or background). If your
application is terminated, the delivery of new location events stops
altogether. Therefore, if your application needs to receive location
events while in the background, it must include the UIBackgroundModes
key (with the location value) in its Info.plist file.
In addition to your delegate object implementing the
locationManager:didUpdateLocations: method, it should also implement
the locationManager:didFailWithError: method to respond to potential
errors.
startMonitoringSignificantLocationChanges when a significant change in position occurs.
it uses cellular or wifi, when it work the location is changed or its called in the particular Time interval.
Discussion
This method initiates the delivery of location events asynchronously,
returning shortly after you call it. Location events are delivered to
your delegate’s locationManager:didUpdateLocations: method. The first
event to be delivered is usually the most recently cached location
event (if any) but may be a newer event in some circumstances.
Obtaining a current location fix may take several additional seconds,
so be sure to check the timestamps on the location events in your
delegate method.
After returning a current location fix, the receiver generates update
events only when a significant change in the user’s location is
detected. For example, it might generate a new event when the device
becomes associated with a different cell tower. It does not rely on
the value in the distanceFilter property to generate events. Calling
this method several times in succession does not automatically result
in new events being generated. Calling stopMonitoringSignificantLocationChanges in between, however, does
cause a new initial event to be sent the next time you call this
method.
If you start this service and your application is subsequently
terminated, the system automatically relaunches the application into
the background if a new event arrives. In such a case, the options
dictionary passed to the locationManager:didUpdateLocations: method of
your application delegate contains the key
UIApplicationLaunchOptionsLocationKey to indicate that your
application was launched because of a location event. Upon relaunch,
you must still configure a location manager object and call this
method to continue receiving location events. When you restart
location services, the current event is delivered to your delegate
immediately. In addition, the location property of your location
manager object is populated with the most recent location object even
before you start location services.
Note:
Apps can expect a notification as soon as the device moves 500
meters or more from its previous notification. It should not expect
notifications more frequently than once every five minutes. If the
device is able to retrieve data from the network, the location manager
is much more likely to deliver notifications in a timely manner.
for differenciate purpose I taken from here
question-2
If we use startUpdatingLocation does it impact on publishing the app to the App Store?
One of the possible reasons for 2.16 rejection is the absence of GPS battery warning in your app description on the app meta in iTunesConnect - "The continued use of GPS may decrease battery life" or something like that.
for More information related to this
Question-3
When the app is killed/suspended (Force closed by the user) it takes some time to restart the location manager from the AppDelegate which causes loss of location data for a period of time. Any possible solution to overcome this
NO, we Can't Over Come, reason the memory newly Initiated.
startMonitoringSignificantLocationChanges
uses cellular/wifi
not accurate but very energy efficient
iOS wakes up your app at least every 15 mins or so to give location update (https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html)
startUpdatingLocation
uses GPS when available
very accurate but eats so much power
Usually, you can submit app using startUpdatingLocation without any problem as long as you state 'Continued use of GPS running in the background can dramatically decrease battery life.' on the app descriptions. And if you put background mode as 'location', use it for location only and not to do anything else.
You don't seem to make use of beginBackgroundTaskWithExpirationHandler, I suggest you use that when your app is in the background.
Obviously, if user does not want your app to get location at all, they can turn off backgroundFetch from the device settings, and touch luck in that case.

LocationManager crash when app wakes for update

Although it works normal when app is active, it crashes when app is terminated and wakes for location update
My code to handle app wakes up for location update on didFinishLaunchingWithOptions
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var glat : String = ""
var glong : String = ""
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsLocationKey] != nil {
let locationManager = CLLocationManager() //or without this line, both crashes
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
let status = CLLocationManager.authorizationStatus()
if (status == CLAuthorizationStatus.AuthorizedAlways) {
locationManager.startMonitoringSignificantLocationChanges()
}
}
return true
}
Here is the locationmanager delegate on AppDelegate
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let lat = manager.location?.coordinate.latitude,
let long = manager.location?.coordinate.longitude {
print(glat + " " + glong)
glat = String(lat)
glong = String(long)
//Line 339
updateloc(String(lat), long: String(long))
}
}
Function to send location info to server
func updateloc(lat : String, long : String) {
let session = NSURLSession.sharedSession()
//Line 354
let request = NSMutableURLRequest(URL: NSURL(string: "URLTO/updateloc.php")!)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = "POST"
let data = "lat=\(lat)&long=\(long)"
request.HTTPBody = data.dataUsingEncoding(NSASCIIStringEncoding)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
let res = response as! NSHTTPURLResponse
dispatch_async(dispatch_get_main_queue(), {
if (res.statusCode >= 200 && res.statusCode < 300)
{
do{
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
var success = 0
if let dictJSON = resultJSON as? [String:AnyObject] {
if let successInteger = dictJSON["success"] as? Int {
success = successInteger
if success == 1
{
print("ok")
}
} else {
print("no 'success' key in the dictionary, or 'success' was not compatible with Int")
}
} else {
print("unknown JSON problem")
}
} catch _{
print("Received not-well-formatted JSON")
}
}
})
}
})
task.resume()
}
Here is the crash log
Crashed: com.apple.main-thread
0 0x10015d998 specialized AppDelegate.updateloc(String, long : String) -> () (AppDelegate.swift:354)
1 0x10015ddf8 specialized AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift:339)
2 0x100159d0c #objc AppDelegate.locationManager(CLLocationManager, didUpdateLocations : [CLLocation]) -> () (AppDelegate.swift)
3 CoreLocation 0x1893d08b8 (null) + 21836
4 CoreLocation 0x1893ccaac (null) + 5952
5 CoreLocation 0x1893c6e48 (null) + 880
6 CoreFoundation 0x18262cf84 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
7 CoreFoundation 0x18262c8bc __CFRunLoopDoBlocks + 308
8 CoreFoundation 0x18262ad04 __CFRunLoopRun + 1960
9 CoreFoundation 0x182554c50 CFRunLoopRunSpecific + 384
10 GraphicsServices 0x183e3c088 GSEventRunModal + 180
11 UIKit 0x18783e088 UIApplicationMain + 204
12 0x10015a324 main (AppDelegate.swift:20)
App crashes when app wakes for a location update in startMonitoringSignificantLocationChanges mode
I really can't see any mistake here. Anyone can help me to fix it ?
As a starting point, I would suggest moving your location manager functionality to a separate class, and having your location class subscribe to the UIApplicationDidFinishLaunchingNotification notification to handle what happens when the app is restarted.
A way to do this would be to have your class a Singleton and have it relay the location updates through the NSNotificationCenter.
startMonitoringSignificantLocationChanges will wake up your app in the background if its enabled when the app gets terminated by the OS.
Depending on what you want to achieve, you could subscribe to two different events and start stop the relevant location services base on the app delegate events.
As a broad (shot in the dark) example:
class LocationCommander {
let locationManager = CLLocationManager()
let defaultCenter = NSNotificationCenter.defaultCenter()
//- NSUserDefaults - LocationServicesControl_KEY to be set to TRUE when user has enabled location services.
let UserDefaults = NSUserDefaults.standardUserDefaults()
let LocationServicesControl_KEY = "LocationServices"
init(){
defaultCenter.addObserver(self, selector: #selector(self.appWillTerminate), name: UIApplicationWillTerminateNotification, object: nil)
defaultCenter.addObserver(self, selector: #selector(self.appIsRelaunched), name: UIApplicationDidFinishLaunchingNotification, object: nil)
}
func appIsRelaunched (notification: NSNotification) {
//- Stops Significant Location Changes services when app is relaunched
self.locationManager.stopMonitoringSignificantLocationChanges()
let ServicesEnabled = self.UserDefaults.boolForKey(self.LocationServicesControl_KEY)
//- Re-Starts Standard Location Services if they have been enabled by the user
if (ServicesEnabled) {
self.updateLocation()
}
}
func appWillTerminate (notification: NSNotification){
let ServicesEnabled = self.UserDefaults.boolForKey(self.LocationServicesControl_KEY)
//- Stops Standard Location Services if they have been enabled
if ServicesEnabled {
self.locationManager.stopUpdatingLocation()
//- Start Significant Location Changes to restart the app
self.locationManager.startMonitoringSignificantLocationChanges()
}
NSUserDefaults.standardUserDefaults().synchronize()
}
func updateLocation () {
if (CLLocationManager.authorizationStatus() == CLAuthorizationStatus.Authorized){
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLDistanceFilterNone
self.locationManager.startUpdatingLocation()
//- Save Location Services ENABLED to NSUserDefaults
self.UserDefaults.setBool(true, forKey: self.LocationServicesControl_KEY)
} else {
//- Unauthorized, requests permissions
}
}
}
Please keep in mind that this code has not been tested, I've extracted segments of an old project which might have some syntax errors due to the updates to the language.
Overall we are doing this:
Subscribe to relevant notifications
When app is about to terminate, check if we are getting location updates, if we are, stop the standard service and start the significant changes service to wake up the app when a new update is available.
When the app wakes up, it will notify the class which will decide if we continue to track notification updates or not (Based on a value stored on NSUserDefaults).
Your app might be crashing due to instantiating a new location manager which then tries to restart location services that might already be running.
I hope this helps!
The documentation says
For services that relaunch the app, the system adds the UIApplicationLaunchOptionsLocationKey key to the options dictionary passed to the app delegate at launch time. When this key is present, you should restart your app’s location services right away. The options dictionary does not include information about the location event itself. You must configure a new location manager object and delegate and start your location services again to receive any pending events.
Furthermore it says:
Calling this method [means startMonitoringSignificantLocationChanges] several times in succession does not automatically result in new events being generated. Calling stopMonitoringSignificantLocationChanges in between, however, does cause a new initial event to be sent the next time you call this method.
This implies more than might be clear on first glance. First, the fact that a location update triggered your app's relaunch doesn't mean you immediately get it via the delegate's locationManager:didUpdateLocations: method. Second, if you are in background (and thus not relaunched, but just became active!), things are yet different again. In fact, a common problem I've seen people encounter is that they don't know whether they're in background (inactive) or terminated/just relaunched. You can't even easily test/see this, because an app that was terminated by the system still shows up when pressing the home button twice. Check the list shown under Attach to Process... in XCode's Debug menu to see whether your app is still running (and in background) or not (btw, terminating an app via the Stop button in XCode results in this inconsistency if you wanna play around with the various ways you can become active or relaunched). I just mention this here because a lot of questions ask "How come X happens when my app gets active again?" and the people answering assume different things than what the asking person meant by that.
Anyways, if your App was in fact relaunched, your locationManager variable contains a brand new instance now, and you should get an update right after calling startMonitoringSignificantLocationChanges. I'll get to the problem you might still encounter then below.
If your App just became active again, you should call stopMonitoringSignificantLocationChanges, then startMonitoringSignificantLocationChanges again to get an update immediately (in short: always stop then start to properly restart, it doesn't hurt even if you weren't running already).
Now to the problem I think that results (in part) from this mess with Apple's way of relaunching after location updates. I'm not entirely sure what's happening without playing around with your code, but I hope this is of some help:
In your application:didFinishLaunchingWithOptions: method you set a local constant with let locationManager = CLLocationManager(). This one then is restarted and triggers location updates. However, it might get deallocated before the delegate's locationManager:didUpdateLocations: method is called, after all, the callback happens asynchronously (and application:didFinishLaunchingWithOptions: might return before that is called). I have no idea how or why this even works and gets as far as you say it does, maybe it's luck, or it has to do with how CLLocationManager internally works. Anyways, what I think then happens is that in locationManager:didUpdateLocations:, you don't get meaningful location data anymore, because the CLLocationManager instance responsible for the call is not valid anymore. lat and long are empty or even nil and your request's URL (which you haven't shown) is invalid.
I would suggest to first:
Ensure that your class's constant locationManager is properly set in application:didFinishLaunchingWithOptions: (right after you checked the presence of the UIApplicationLaunchOptionsLocationKey flag). Get rid of the local constant of the same name.
Stop and then restart the location updates. This should call your delegate's locationManager:didUpdateLocations: method immediately with the latest location data (the one that triggered your app's relaunch or becoming active again).
Ensure you get meaningful data in your locationManager:didUpdateLocations: method. Don't call updateloc if that's not the case (you might also want to move the line print(glat + " " + glong) to after you set the class's variables to the new values, so you see on the console whether what you get makes sense).
There"s one small thing I can't tell on the top of my head: If your app is just getting active again (and not relaunched) I'm not sure whether the UIApplicationLaunchOptionsLocationKey is even set at all. You might wanna investigate that one, too. If becoming active isn't a "relaunch" in that sense, though, your locationManager should still be happy an working anyways, at least that's what I get from the documentation (and it's been a while since I've tried that myself). Let me/us know if you're still having problems, I'm curious how it'll turn out. :)
Try this
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_DEFAULT.rawValue), 0)) {
updateloc(String(lat), long: String(long))
}

How to Receive Darwin Notifications when app is in background

App is configured to receive location updates while in the background so as to keep the app active and the updates are being received successfully when app is in the background.
Darwin notifications have also been configured and are received only when the app is the current app in the foreground. As soon as the app is put in the background it stops receiving the Darwin Notifications.
Any thoughts on how to receive the Darwin Notifications while the app is in the background?
Code snippets below.
Building App in Swift2
in appdeligate
let callback: #convention(c)
(CFNotificationCenter!, UnsafeMutablePointer<Void>, CFString!, UnsafePointer<Void>, CFDictionary!) -> Void = {
(center, observer, name, object, userInfo) in
//Execute callback code
}
let exCb: CFNotificationCallback = unsafeBitCast(callback, CFNotificationCallback.self)
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),nil ,exCb,"com.apple.springboard.hasBlankedScreen" as CFString,nil ,CFNotificationSuspensionBehavior.DeliverImmediately)
in viewcontroller
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
//locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
Make sure you've activated the Location updates capability in Background Modes.
This will allow your app to receive location changes in the background and proceed with your logic.
A great library for this kind of functionality is INTULocationManager.

CLCircularRegion and wake up app

In application we have mechanism like native Reminder app in iOS with firing notifications when user enter or exit in some region.
But two devices behave differently (5 and 5s) in same time. All devices have enable notifications, and allow use locations.
Two devices have a some "travel" and in the route created 10 points. First device (5) when came to finish received only 6 notifications, (5s) don't receive any notification.
But my question is how I can know when my app is restart in background or continue working. Because, all log in app I redirect into a file, and after download container and analyze what happened in app in travel time.
I noticed app restart in same times when device is enter to region and my log marks fired in the file but notifications don't receive. This is happended when app try to get some information from web service in didFinishLaunchingWithOptions
And maybe this is problem. How to know distinguish restart app or continue working. Thx.
Are you checking UIApplicationLaunchOptionsLocationKey in didFinishLaunchingWithOptions similar to (sorry, Swift is what I have now):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsLocationKey] != nil {
// app was launched in response to incoming location event
}
}
Additionally, if you're not already doing this you may need to create notifications differently if app is in background:
// Show an alert if application is active
if UIApplication.sharedApplication().applicationState == .Active {
if let message = notefromRegionIdentifier(region.identifier) {
if let viewController = window?.rootViewController {
showSimpleAlertWithTitle(nil, message: message, viewController: viewController)
}
}
}
else {
// Otherwise present a local notification:
let notification = UILocalNotification()
notification.alertBody = notefromRegionIdentifier(region.identifier)
notification.soundName = "Default";
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
}

Update data model on Apple Watch when phone app is in background

I have written an iOS app that refreshes its data model when a push notification is received whilst the app is in the foreground, once the data is retrieved from the server I send that information to the watch kit app using:
// This code resides in ErrorsViewController.swift
func updateWatchContext() {
do {
let messages = convertParseObjectsToJSON(tasks)
try session?.updateApplicationContext(["messages" : messages])
} catch let error as NSError {
NSLog("Updating the context failed: " + error.localizedDescription)
}
}
func convertParseObjectsToJSON(data:[PFObject])->[[String : AnyObject]]
{
var data = [[String:AnyObject]]()
for var i = 0; i < tasks.count; i++
{
let object = tasks[i]
data.append([
"createDate" : object["createDate"],
"errorMessage" : object["errorCode"]
])
}
return data
}
This works fine when the application is in the foreground, the data model gets updated on the watch as expected. However in the scenario that the phone is running in the background, how can I make a background fetch, parse the data and send it to the watchkit app without waking the iPhone, using watch connectivity?
I was thinking about trying to add the code in AppDelegate, but I don't believe that will work. I'd like to note that I do not want to make any network requests directly from the watch itself due to the limited CPU power; it would be un-necessary to handle the data parsing there.

Resources