19 - Maps, Core Location, Background
MapKit for SwiftUI - 2023 WWDC
Apple docs: MapKit for SwiftUI
Apple released MapKit for SwiftUI with iOS 17 - much better integration with SwiftUI, etc.
Before that - only simplistic functionality was provided and more comprehensive usage was done using older MKMapView wrapped into UIViewRepresentable.
Initialize Map
Just add Map()
element to your view.
Coordinates
1 2 3 4 |
|
Markers
Basic marker
1 2 |
|
Color the marker balloon with .tint()
.
Basic marker, custom icon inside marker balloon
1 2 |
|
Use SF Symbols
app to explore all the possible icons.
Annotations
Annotation allows you to use a custom view to mark specific locations on a map.
1 2 3 4 5 6 7 8 |
|
The anchor option within Annotations provides you with the capability to adjust the marker’s anchor point in various directions relative to the provided coordinates.
You have the flexibility to use a combination of top, bottom, leading, right, or useCGPoint for precise control. By default, the anchor point is set to the center.
1 |
|
Map Controls
1 2 3 4 5 6 7 8 |
|
- MapCompass — Shows the current orientation of the map
- MapPitchToggle — Toggles between flat and pitched map
- MapPitchSlider (Mac only) — Slider to control the pitch of the map
- MapScaleView — Shows legend with distance information
- MapUserLocationButton — Centers the map on the user’s location
- MapZoomStepper (Mac only) — Button to adjust the map zoom level
- MapLocationCompass (WatchOS only) — Combined user location button and map compass
Map style
- Standard — Street map with road and names of places
- Imagery — Satellite image of the area
- Hybrid — Satellite image of the area including road and names of places
All map styles offer support for automatic, flat, and realistic (3D) elevation. Additionally, the standard and hybrid map styles also provide information on current traffic conditions.
1 2 |
|
PolyLine
1 2 3 4 |
|
or
1 2 3 4 |
|
MapCircle, MapPolygon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Core Location services
- SwiftUI continuously creates and destroys views. When there is a state change, all views depending on it will be destroyed and recreated. This happens quite aggressively throughout the lifecycle of SwiftUI apps
- SwiftUI provides us with specific property wrappers that store their values somewhere else rather than on the views directly.
Two of such wrappers are @StateObject and @ObservedObject. - ViewModel will be responsible for receiving Core Location updates and providing info about location to view
- Dealing with location requires user granted permission!
Minimal ViewModel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Requesting access to location
1 2 3 |
|
This causes dialog to pop-up where user can allow location access. If denied - can later be changed through settings.
If there are issues in simulator (app not visible in settings), try this from terminal to reset the permissions (relaunc app again after).
1 |
|
Also add callback to change authorization property in vm.
1 2 3 |
|
RequestLocationView & ErrorView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
TrackingView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
EnvironmentObject
@EnvironmentObject is a simpler way of using @ObservedObject on multiple sub-views. Rather than creating some data in view A, then passing it to view B, then view C, then view D before finally using it, create data in view A and put it into the environment so that views B, C, and D will automatically have access to it.
Like @ObservedObject, never assign a value to an @EnvironmentObject property. Instead, it should be passed in from elsewhere, and ultimately initialized with @StateObject to create it .
Environment objects must be supplied by an ancestor view – if SwiftUI can’t find an environment object of the correct type you’ll get a crash. This applies for previews too!
Receive location updates
Full viewmodel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
NB! You can include as many optional bindings and Boolean conditions in a single if statement as you need to, separated by commas. If any of the values in the optional bindings are nil or any Boolean condition evaluates to false, the whole if statement’s condition is considered to be false.
1 2 3 |
|
is the same as
1 2 3 4 5 |
|
PairView
1 2 3 4 5 6 7 8 9 10 11 12 |
|
App settings for location usage
Depending on location usage needed, these keys are mandatory in Info.plist (xml file with app settings).
Values are used to inform user in permission granting dialog, why app needs location access.
1 2 3 |
|
NB! Info.plist was removed in XCode 13. Add keys via XCode UI.
Receiving location updates while in background
Keepin app running in background is extremely limited and controlled in iOS. Luckiliy, location updates is one of the features that is allowed.
Set up neccessary settings in XCode / Info.plist. Navigate to capabilities section and click "+" on upper right corner.
Choose Bakground Modes - new section is addedd to settings.
Activate Location updates.
also, you need to
1 |
|
and
1 2 3 |
|
Now when app starts to listen for location updates while in foreground and then enters background - app is kept running.
Links
- https://developer.apple.com/documentation/corelocation/requesting_authorization_for_location_services
- https://developer.apple.com/documentation/corelocation/cllocationmanager/1620551-requestalwaysauthorization
- https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_standard_location_service
- https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/handling_location_events_in_the_background
Using Google maps in SwiftUI
https://developers.google.com/codelabs/maps-platform/maps-platform-ios-swiftui