iOS 10 absolutely fails to map points in background mode with Appleās built-in GPS. However, that very same code works perfectly well with third-party GPS APIs like the one from Dual. So, has this been tested in background mode?
Again, I ask because background mode has never worked with the previous code or any mapping code that I have written that worked perfectly well in iOS versions prior to iOS 10. I know from previous threads and PM messages that Iām not alone in this observation. I have repeatedly asked Apple about this issue but have only heard crickets.
I didnāt test heavily on the device, though what testing I did did capture points when I backgrounded the app. There was jitter in the timing of the delivery of location updates to the delegate method but, at least in my limited testing, it didnāt lose any points.
(BTW, great tutorial. THANKS! Iām just seeing the iOS 11 update now. I read a previous version and it was very helpful. Signed up just to share my experience with location manager in background mode.)
iOS 10 adds some additional requirements to get background location updatesā¦
I was able to reliably get background updates in iOS 10 doing these things:
add this key to your info.plist:
<key>NSLocationAlwaysUsageDescription</key> <string>NSApp uses your location to keep the gub'mint informed of where you are at all times!</string>
(If youāre going through Xcode, I think it calls this āPrivacy - Location Always Usage Descriptionā)
Where the tutorial calls locationManager.requestWhenInUseAuthorization() you should instead call locationManager.requestAlwaysAuthorization()
Iām being facetious about the message in info.plist! The point is, this message is displayed to the user with an Allow/Donāt Allow prompt. You should put some thought in to it. If a user canāt immediately see why they should click āAllowā it, they will not do it and your app wonāt work. You get one shot at this.
The message is displayed at the time requestAlwaysAuthorization() is called. You should call it at a time where it will make immediate sense to the user why the app wants their locationā¦ e.g., right after the user clicks a button like, āstart tracking my hikeā.
The message is displayed after the user has installed your app, but hasnāt yet been asked. After the user has answered, they wonāt be asked every time. If the user initially answers āAllowā then something like a week later iOS will prompt the user to verify whether they want to continue to allow background tracking. So you really want to think about the message the user will see.
I added a reply about how to get background mode working in iOS 10, but I think I added it to the main thread rather than reply to your post. In case that doesnāt get a notification sent to you Iām now replying to your post.
Anyone get this working with only Kilometers? I keep changing the UnitSpeed values and the Measurements to kilometers but on phones other than a spanish-speaking country it shows as meters.
Also, any way to keep the decimal points to 2 values only? everything returns .000
Edit: Got this working with Google Maps in case anyone is interested.
Hi
I am pretty new to iOS programming.
I have a problem even getting started here.
Why is the HomeViewController.swift completely empty??
Wouldnāt the segue at least be present in the file??
Thanks
HomeViewController.swift isnāt actually needed at all; default UIViewController functionality is all that is required. I left it in the sample project to make it easier for readers should they decid to make an enhancement that would require it to exist.
The segues are defined, as always, in the storyboard. You only need to write prepare(for:sender:) if you actually need to gain control and execute some code (e.g. to inject model data into the destination view controller). In this app, the home screen is just a menu and pressing one of the buttons just displays the desired view. Each of the relevant view controllers it displays is completely standalone.
Thanks for the quick response. When I set my locale to Serbia, I get meters (well, km actually) and not miles. Where are you seeing miles displayed (other than the Pace, which is expected to be in min/mi unless youāve changed the code as described in part 1 of the tutorial)? I donāt recognize the screenshot you included.
Hi Richard
Iāve been going through your code, trying to grasp what you have made, and I have a few questions I would appreciate if you would elaborate upon.
1: In the NewRunViewController you makeā¦.
var run: Run?
This must be a variable of type Run-table in the SQLite database. I know the class is auto-generated when you create the tables, but I have not seen this way of working with SQLite in swift before ā would you please point me in the right direction for instructions?
2: in func mapRegion:
A variable instantiation separated by a comma??? what is that?,
In the same functionI I understand run.distance, as ādistanceā is a field in the run-table, but how can you call run.locations? is it because you have connected them in the inverse relationship??
3: In NewRunViewController I donāt understand the ācontinueā in the else statement (last line)
wouldnāt that mean, that if the condition is not met with the guard, then youā¦ well simply continue, and if that is the case, what is the purpose of guard?
Actually, itās Core Data. Yes, Core Data is using a SQLite store under the hood but app doesnāt use SQLite directly. Core Data is a massive topic and there are some great tutorials, both written and video, on the site to help get you up to speed on it. I especially recommend Luke Parhamās āBeginning Core Dataā video series.
Those arenāt variable instantiations. The first is an optional binding (a Swift-specific language construct) and the second is just another clause in the guard statement. In Swift, you list all of your conditions in guard or if let statement separated by commas rather than having multiple, nested statements. That statement translates in English to āmake sure run.locations is not nil, let me refer to it for the rest of this function as locations, and make sure there are some locations there. If any of that that is not true, return nil.ā
The continue applies to the containing for .. in loop. What it will do is skip a location if it is not sufficiently accurate or recent but will continue processing other locations in the update. A failed guard must exit at least itās enclosing scope, which in this case is the for .. in loop. continue is a common way to āexitā the loop for the current iteration and move on to the next.
When Iām implementing the segue portion of the code in NewRunViewController, I get an error: āOverriding āprepareā must be as available as the declaration it overrides.ā All the necessary files associated with the application are accounted for, is there something that Iām missing? Is this a Swift 3.0 syntax issue? Any help would be sincerely appreciated.
I donāt see that error in my copy of the sources. The only way I know to trigger it is if you accidentally included a private on the declaration of prepare(for:sender:). In other words, if you did: