The CoreLocation framework is utilized to provide services that determine a device's geographic location, altitude, and orientation. It can also determine its position relative to a nearby iBeacon device. We can use CoreLocation to center on the user's specific location on a map.
Basic Code Sample
Setting up a map is pretty easy. The basic code is as simple as this:
Map(coordinateRegion: Binding<MKCoordinateRegion>)Notice that it needs a binding, commonly a @State variable of type MKCoordinateRegion. To create one, we need to look at the basic requirements for MKCoordinateRegion.
MKCoordinateRegion(
center: CLLocationCoordinate2D,
span: MKCoordinateSpan
)CLLocationCoordinate2D is the longitude and latitude of a specific coordinate on the map.
CLLocationCoordinate2D(
latitude: 37.789467,
longitude: -122.416772
)MKCoordinateSpan is the width and height of the map view in degrees, using latitudeDelta and longitudeDelta. A higher value creates a wider, zoomed-out view, while a smaller value creates a more precise, zoomed-in view.
MKCoordinateSpan(
latitudeDelta: 0.5,
longitudeDelta: 0.5
)Given both pieces, you can create an MKCoordinateRegion like this:
@State var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: 37.789467,
longitude: -122.416772
),
span: MKCoordinateSpan(
latitudeDelta: 0.5,
longitudeDelta: 0.5
)
)The problem is that we do not know the exact coordinates of the user. This is where CoreLocation comes to the rescue.
Ask for location permission
To set up CoreLocation, first ask for permission to access location data. This is done by editing the Info.plist file.
Inside Info.plist, add a new row named Privacy - Location When In Use Usage Description. Put any clear string value in the value section. This text is shown when the app asks the user for location permission.
Important: if the permission text is missing, the app can fail when requesting location access. Add the usage description before calling requestWhenInUseAuthorization().
Create a LocationManager
CoreLocation is a bit tricky in SwiftUI compared to UIKit because we need to set a CLLocationManagerDelegate to access location updates. This is ideally handled by a custom class.
Create a new file named LocationManager. Import MapKit and CoreLocation, then make the class conform to CLLocationManagerDelegate and ObservableObject.
import MapKit
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {
// code here
}CLLocationManagerDelegate gives us access to location updates, while ObservableObject lets SwiftUI observe the location region as it changes.
Next, create a published variable of type MKCoordinateRegion. This is the binding value that will update when the user's location changes.
@Published var region = MKCoordinateRegion()Then create a CLLocationManager instance to set up and handle location tracking.
private let manager = CLLocationManager()Override init, set the location manager delegate to self, set the desired accuracy, ask for authorization, and begin updating the location.
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}Since the class conforms to CLLocationManagerDelegate, add the delegate method that receives updated locations.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// code here
}Inside this method, use the last reported location and update the map region.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last.map {
region = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: $0.coordinate.latitude,
longitude: $0.coordinate.longitude
),
span: MKCoordinateSpan(
latitudeDelta: 0.5,
longitudeDelta: 0.5
)
)
}
}Here is the full manager in one place:
import MapKit
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {
@Published var region = MKCoordinateRegion()
private let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last.map {
region = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: $0.coordinate.latitude,
longitude: $0.coordinate.longitude
),
span: MKCoordinateSpan(
latitudeDelta: 0.5,
longitudeDelta: 0.5
)
)
}
}
}Use LocationManager in SwiftUI
Go back to ContentView.swift and create a @StateObject for your location manager.
@StateObject var manager = LocationManager()You can now use the manager's region binding in your map.
Map(coordinateRegion: $manager.region)Map Options
Although using only coordinateRegion is enough to display a basic map, it will only center around the specified location. It will not show a marker for where you are.
For more control, use a map initializer that includes interaction modes, user location, and tracking mode.
Map(
coordinateRegion: Binding<MKCoordinateRegion>,
interactionModes: MapInteractionModes,
showsUserLocation: Bool,
userTrackingMode: Binding<MapUserTrackingMode?>
)interactionModescontrols how users interact with the map. Use.pan,.zoom, or.all.showsUserLocationcontrols whether the map shows the user's location marker.userTrackingModecontrols whether the map follows the user's location changes.
Because userTrackingMode is a binding, set it as a separate state value.
@State var tracking: MapUserTrackingMode = .followThen pass the tracking binding into the map.
Map(
coordinateRegion: $manager.region,
interactionModes: MapInteractionModes.all,
showsUserLocation: true,
userTrackingMode: $tracking
)Simulate Location
Simulating location in SwiftUI is a bit different compared to UIKit. In UIKit, you needed to set the simulated location at the scheme level. In SwiftUI, you can change simulated location on the fly even while the simulator is running.
Select the Swift file that shows the map, such as ContentView.swift. Then navigate to Debug -> Simulate Location and select a location.
Tip: location simulation is useful for testing permissions, region updates, user tracking, and map movement without needing to test on a physical route.