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:
- Significant locations deliver updates only when there has been a
significant change in the device’s location, (experimentally
established 500 meters or more) - 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. - 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 withbeginBackgroundTask(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
Uibutton Target Action Inside Uiview
How to Deal with Non-Optional Values in Nsuserdefaults in Swift
Get Path to Subdirectory in Resources Folder
Uitableviewcells Initial Load View/Display Issue
iOS UIlabel Autoshrink So Word Doesn't Truncate to Two Lines
Xcode 8.3 Swift Version Error (Swift_Version) in Objective C Project
How to Go to Specific Native View Controller from React-Native Code
Svg Viewbox Height Issue on iOS Safari
Load Custom Error HTMLstring When Wkwebview Loadrequest Fails
How to Add a Lock Screen Widget (Requiring iOS 16) and Still Support iOS 15
Random Glitchy Rendering of Svg on iOS/Wkwebview
Bootstrap Columns with Flexbox Are Not Taking Proper Width on iOS and Safari
"Reached the Max Number of Texture Atlases, Can Not Allocate More" Using Google Maps
Save The Exif Metadata Using The New PHPhotolibrary
How to Keep Animated Gifs Animated While Scrolling on iOS Devices