iOS - Running Background Task for Update User Location Using Swift

IOS - Running background task for update user location using swift

I am currently doing kind of the same thing as my first iOS-project - what helped me was this :
Periodic iOS background location updates

And from there on I found a Swift-Port of https://github.com/voyage11/Location: https://github.com/silento/Location. In the project from voyage11, there is also linked a blog post that a bit of clarifies what is happening and that also links to a second one that tells how to restart location services using "significant location changes". I am currently trying to implement this with "region location monitoring", but not sure if it will work.

Oh, and I moved the code that triggers a new background task out of the didUpdateLocation-part to the restartLocationUpdates-part - so you can be sure your task is restarted even if you do not get a location in time.

It did not work from the start, but when I added the code from https://stackoverflow.com/a/19085518/3085985 as a replacement for the stopUpdatingLocation() and startUpdatingLocation() - parts.

It is really more complicated than it should be and what I am used from Android - but it is how it is. If you are developing for Iphone5+ you can also consider this post (I could not test it right now as I only have an iPhone 4S and the feature is not supported in the Simulator): https://stackoverflow.com/a/24666487/3085985.

I hope you got some starting points, I am currently still testing my solution but it seems to work as far as I can see now ;)

P.S.: I somewhere read something about background tasks sleeping for more than 5 minutes being canceled - as I only wait 30 seconds I do not have this issue, but you might have. But maybe keeping location services running with a really high distance prevents this from happening.

P.P.S.: As Swift is new you should get used to reading Objective C-Code and trying to translate it to Swift. It often is the only way to find good tutorials.

One more thing - for iOS8 you need something like :

if (self.locationManager.respondsToSelector(Selector("requestWhenInUseAuthorization"))) {
// on iOS8+, we need to request the location authorization ourselves -.-

self.locationManager.requestAlwaysAuthorization()
}

And don't forget to add the "NSLocationAlwaysUsageDescription" to your Info.plist as well as allowing background location updates in your project capabilities.
Sorry for this mess of text, but it is a really complicated topic ;)

edit: Regarding your first comment :
You should create a Swift project, e.g. with a single window and in Main. storyboard add a switch. Then you should implement a ToggleActionChanged-event enabling or disabling the listeners, depending on the new state of the switch. I cannot explain all the basics, you should watch a basic tutorial, I like https://www.youtube.com/playlist?list=PL_4rJ_acBNMHa_RGZigG2nQI3aL1kTa4r and used especially the 7th video as an example of how to build a basic app. The only thing that can be made easier than shown there is using right-click-dragging from the storyboard to the code view instead of implementing some methods by hand. There is a handy assistant.

For your location event listeners you can add a new swift class and name it e.g. LocationDelegate.swift . It makes it a bit more clean. I use a Singleton-pattern for that, you can look up how to do this. Or you could use the structure of the github-project I posted above. I can't provide the complete source as you need to change it so it fits your individual needs ;)

Oh, and one more thing : To remember if your switch is enabled between application launches, you can use something like this :

   @IBAction func toggleTrackingChanged(sender: UISwitch) {

// This is your method for when the switch changed... put your location activation/deactivation here

let userDefaults = NSUserDefaults.standardUserDefaults()

userDefaults.setBool(sender.on, forKey: "ODLTrackingActive")
}

}

and in your viewDidLoad-Method :

let userDefaults = NSUserDefaults.standardUserDefaults()

var trackingActive = userDefaults.boolForKey("ODLTrackingActive")

toggleTrackingSwitch.setOn(trackingActive, animated: false)

// reactivate location tracking if necessary

Good luck!

Best way to use background location updates in iOS (Swift)

that sends the user's location via API every 3 or 5 minutes, while the
app is backgrounded

Does not make much sense. What if user stands in same location for 5 minutes? you will make same location entry in your server multiple times? Isn't it better rather than updating location at certain interval, if you could update your server with user location once user's location changes??

So use locationManager, set its delegates and start updating your server via API when user location changes rather than at regular interval.

I don't know which is the best way to do this, or if it's even
possible because of Apple's strict background rules

Its absolutely possible. All you have to do is to opt for Location Updates capability in Xcode.

Now whats the best way? Its a relative term. This depends on how your app will use the users location info. If you unnecessarily start observing users location, you will unnecessarily drain the users iPhone battery, hence apple will reject your app.

How apple process's app using location update capability?

Its pretty simple. Location updates capability comes with the cost, that if your app observes user location updates accurately in background, it will drain out the device battery. Because its a costly trade off, apple expects you to use this only if its necessary for your app.

For example : If you are making a map app or an app that tracks the users location changes and then later plots on a map or something and lets the user know about his movement (like running apps) its absolutely fine to use location update capability because you are adding value to user.

If you try to think creepy! and try to use the location update just to keep your app alive and do some thing completely unrelated to location update in background app will reject your app. Like few developers try to be over smart and use location updates to keep their app in sync with server or to upload/download files at regular interval or something like that which are no way related to location updates, apple will reject such apps.

So no way to be creepy? And use location updates to do something useful which is not related to location itself?

Yes, you can. Apple is generous enough to allow that. For example : Dropbox uploads your images in background, when you move from one location to another. All it does is, it looks for user location changes, once location changes delegates triggers, creates a upload task with background session and starts uploading files.

How to do that?

You can still use the location manager. All you have to do is to use

locationManager.startMonitoringSignificantLocationChanges()

This will trigger your location delegates only when there is a significant location changes :)

On the other hand, if your app actually makes use of user locations and use

startLocationUpdates()

make sure you dont consume location updates unnecessarily. Make sure u put distance filter properly according to your apps requirements. Dont be greedy and dont waste iOS resources unnecessarily and apple will be happy and will not trouble you :)

Conclusion:

If your app actually makes use of location and adds some value to user (like map app/running apps/travel apps) only then use startLocationUpdates and use distance filters properly (optional, but good to have it).

If you are being creepy always use startMonitoringSignificantLocationChanges else app is bound to get rejected

how to update user location in both foreground and background every 10 seconds?

I had a similar app to be built some time ago, and I also tried things like you did, but that is very very wrong way to use timer and all to record your location.

First of all, I suggest you to not try to record/ping location explicitly because, it will cause batter drainage. There is something called, significant location change, please go through it in docs. This will trigger you location update in every single significant location change.

But I assume, you really need to ping the location. There is a git repo, link below

Background geo location tracking

Please go though it, it is really nice library out there, I have ever come across, go through its documents. I am sure you will come around and be able to use it for your app. :)

Send Location of User in Background Swift

class ViewController: UIViewController, CLLocationManagerDelegate {
private var locman = CLLocationManager()
private var startTime: Date? //An instance variable, will be used as a previous location time.

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

guard let loc = locations.last else { return }

let time = loc.timestamp

guard var startTime = startTime else {
self.startTime = time // Saving time of first location, so we could use it to compare later with second location time.
return //Returning from this function, as at this moment we don't have second location.
}

let elapsed = time.timeIntervalSince(startTime) // Calculating time interval between first and second (previously saved) locations timestamps.

if elapsed > 30 { //If time interval is more than 30 seconds
print("Upload updated location to server")
updateUser(location: loc) //user function which uploads user location or coordinate to server.

startTime = time //Changing our timestamp of previous location to timestamp of location we already uploaded.

}
}

Background Location Updates

@onurgenes, If you add a real code from your project, first of all, how you can start any location updates from here?

    if let _ = launchOptions?[.location] {
locationManager = LocationManager()
locationManager?.delegate = self
locationManager?.getCurrentLocation()
return true
}

When app start at for the first time launchOptions will be nil, and your LocationManager() even not started, so your any location monitoring and updates will not work (maybe you have the some code at app.start() but now it looks like an error).

The second thing - at your sample, you are using bought of locating monitoring:

    locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()

so here your location manager handles only significantLocationChanges(). If you want to use both of them - you should toggle it (at didBecomeActiveNotification and didEnterBackgroundNotification) or create different instances of the location manager as Apple recommend.

The third - your problem. Let's look for this part more detailed:

locationManager = LocationManager()
locationManager?.delegate = self
locationManager?.getCurrentLocation()

As I mention up - at LocationManager() you start monitoring location:

locationManager.startUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()

and this is what you need - significant location changes. But after you call getCurrentLocation() with locationManager.startUpdatingLocation() so you 'rewrite' your monitoring, and that's the reason why you did not get any updates from it.

Also, take in mind:

  1. Significant locations deliver updates only when there has been a
    significant change in the device’s location, (experimentally
    established 500 meters or more)
  2. Significant locations very inaccurate (for me sometimes it up to 900 meters). Very often significant location use just for wake up an application and restart
    location service.
  3. After your app wake up from location changes
    notification, you are given a small amount of time (around 10
    seconds), so if you need more time to send location to the server
    you should request more time with
    beginBackgroundTask(withName:expirationHandler:)

Hope my answer was helpful. Happy coding!

Run a Swift 2.0 app forever in background to update location to server

Periodic location updates are a bit tricky in IOS.There's a good thread that discusses this, you can read more here

iOS will terminate your app after a few minutes, regardless if your timer is running or not. There is a way around this though, I had to do something similar when writing an ionic app so you can check out the code for this here, that link has a swift class that manages the periodic location updates in iOs.

In order to get periodic locations in the background, and not drain the battery of the device, you need to play with the accuracy of the location records, lower the accuracy of the location manager setting its desiredAccuracy to kCLLocationAccuracyThreeKilometers, then, every 60 seconds you need to change the accuracy to kCLLocationAccuracyBest, this will enable the delegate to get a new, accurate location update, then revert the accuracy back to low. The timer needs to be initialized every time an update is received.

There's also a way to wake up the app in the background after its been killed by the user, use the app delegate to have the app listen for significant changes in location before its killed. This will wake up the app in the background when the user's location makes a big jump (can be around 200ms). When the app wakes up, stop monitoring for significant changes and restart the location services as usual to continue the periodic updates.

Hope this helps.

Update

In Swift 2 you'll also need:

self.locationManager.allowsBackgroundLocationUpdates = true


Related Topics



Leave a reply



Submit