Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic location based local notifications Swift iOS

I am trying to fetch some arrival times for buses when a user approaches a stop, I have tested to ensure that the regions are correctly being trigged by sending a basic local notification and I have also tested my web service call to ensure it is working properly.

However I am having a hard time fetching the info then sending a notification.

Here is my code:

    var bgTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier()

    bgTask = UIApplication.shared().beginBackgroundTask {

        self.webService?.getStopEstimates(routeStopIds: stopRouteIdSet, routeNameDict: routeNameDict, completion: { (result) in

            if result == "error" {
                return
            }

            let notification = UILocalNotification()
            notification.fireDate = Date(timeIntervalSinceNow: 1)
            notification.alertTitle = region.identifier + " Stop Details"
            notification.alertBody = result
            notification.soundName = UILocalNotificationDefaultSoundName
            UIApplication.shared().scheduleLocalNotification(notification)
        })

        UIApplication.shared().endBackgroundTask(bgTask)
    }

Any one see why it might not be sending? I have enabled background fetch and location services. This is inside didEnterRegion

like image 233
Clip Avatar asked Aug 22 '16 23:08

Clip


2 Answers

I did the same thing (when a user approach a store that has a discount he/she will be notified).

First of all I would I would suggest you to download all the data and save them on Core Data or SQLite if that is an option.

Second take a look at Significant Location Change here. It will update your location in the background each 500 meters if you are moving and it will save a lot of battery instead of the didUpdateLocation.

Third after using SLC, on each call, fetch from your Core Data or SQLite the 20 nearest locations of your current location and add them for monitoring(Use Haversine to calculate all the points distances from your current location and then get the 20 nearest). Each time SCL is called update the regions that you monitor(if saving your data offline is not an option I would recommend to send the request at your webservice here. The ideal scenario is that you will send your location to your webservice and it will reply back with the nearest points. then add them for monitoring)

When the didEnterRegion called make sure you have your data downloaded and then create a local notification.

Let me know if you need more details

PS. Have in mind that your app might be terminated (in background) by the system if is consuming a lot of resources. In that case you have to reinitialise everything you need for pulling and showing the data (Networks requests, Location manager etc.) You can detect that the App relaunched because of a location event in AppDelegate's didFinisingLaunchingWithOptions:

  if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {}
like image 149
BlackM Avatar answered Nov 07 '22 15:11

BlackM


check this example on GitHub because I think your issue here is how you are handling the background task. If the web service call took too long, the background task will be invalidated. I don't know how well you know ObjC but in the example above, you will find a BackgroundTaskManager class handling that for you.

Things you should make sure of are:

  1. Background Fetch in Target Settings (Background Modes section) are ON.
  2. You have asked and obtained "always" authorization (in plist)
  3. Location Updates in Target Settings (Background Modes section) are ON.
  4. Registered for User Notifications in appDidFinishLaunching in application delegate.
  5. Renew expired background tasks if service took too long.

In the case of calling startUpdatingLocation while your app is in the background, CoreLocation still responds by starting the location updates for your app, but the system will no longer hold an assertion for you, and your app will be suspended once the allowed time in the background is spent. This time is (currently) is 10 seconds. But actually your app is now suspended and can no longer receive the updates. There is a way to extend this duration to (currently) up to 3 minutes by employing beginBackgroundTaskWithExpirationHandler: read more about it in Apple's CoreLocation Documentation

Whether this extended time is enough will depend on your app.

For your use case, Significant Location Change service is quite efficient. You can start location updates when your app is in the foreground and defer location updates while your app is in the background. You can also read Apple's documentation about Significant Location Change

like image 28
TechSeeko Avatar answered Nov 07 '22 15:11

TechSeeko