How to get Current Location with SwiftUI?
Asked Answered
A

3

13

Trying to get current location with using swiftUI. Below code, couldn't initialize with didUpdateLocations delegate.

class GetLocation : BindableObject {
    var didChange = PassthroughSubject<GetLocation,Never>()

    var location : CLLocation {
        didSet {
            didChange.send(self)
        }
    }
    init() {}
}
Ainslee answered 10/6, 2019 at 20:22 Comment(1)
Why you wasn't able to create the CLLocationManager in this class?Hemielytron
H
8

This code below works (Not production ready). Implementing the CLLocationManagerDelegate works fine and the lastKnownLocation is updated accordingly.

Don't forget to set the NSLocationWhenInUseUsageDescription in your Info.plist

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()
    var lastKnownLocation: CLLocation?

    func startUpdating() {
        manager.delegate = self
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        print(locations)
        lastKnownLocation = locations.last
    }

    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == .authorizedWhenInUse {
            manager.startUpdatingLocation()
        }
    }
}
Hydrothorax answered 11/6, 2019 at 9:36 Comment(2)
For anybody getting the error: Use of unresolved identifier 'PassthroughSubject', make sure you add "import Combine" to the top of your file.Puff
what specifically makes this code not production ready? I assume the handling of rejected location permissions is one reason?Pipkin
D
5

I have written a one-file swift package with usage instructions on https://github.com/himbeles/LocationProvider. It provides a ObservableObject-type wrapper class for CLLocationManager and its delegate. There is a @Published property location which can directly be used in SwiftUI, as well as a PassthroughSubject<CLLocation, Never> called locationWillChange that you can subscribe to via Combine. Both update on every didUpdateLocations event of the CLLocationManager.

It also handles the case where location access has previously been denied: The default behavior is to present the user with a request to enable access in the app settings and a link to go there.

In SwiftUI (> iOS 14, > macOS 11), use as

import SwiftUI
import LocationProvider

struct ContentView: View {
    @StateObject var locationProvider = LocationProvider()
    
    var body: some View {
        VStack{
            Text("latitude \(locationProvider.location?.coordinate.latitude ?? 0)")
            Text("longitude \(locationProvider.location?.coordinate.longitude ?? 0)")
        }
        .onAppear {
            do {try locationProvider.start()} 
            catch {
                print("No location access.")
                locationProvider.requestAuthorization()
            }
        }
    }
}
Demetria answered 3/3, 2020 at 6:12 Comment(3)
This not work for me on iOS 14, app crashes with this error => 'Invalid parameter not satisfying: !stayUp || CLClientIsBackgroundable(internal->fClient)Picco
@Picco Seems, like there might be a problem with the background permissions? Have you added Location to UIBackgroundModes as described in the README.md of the package? I have set up a minimum working SwiftUI example using Xcode 12 beta 6 and it is running as intended both on device and in the simulator. See github.com/himbeles/LocationProviderExampleDemetria
@ lsrggr, No I did not, that's must be it, Thank you but I used "Paul Hutson" method for location anyway.Picco
I
3

As of Xcode 11 beta 4, you will need to change didChange to willChange:

var willChange = PassthroughSubject<LocationManager, Never>()

var lastKnownLocation: CLLocation? {
    willSet {
        willChange.send(self)
    }
}
Inwardly answered 23/7, 2019 at 15:36 Comment(2)
Also, on Xcode11.1 you will need to change BindableObject to ObservableObject-Debonair
I also needed to prepend the lastKnownLocation declaration with @Published (XCode 12.0)Encrata

© 2022 - 2024 — McMap. All rights reserved.