iosswiftuicore-locationlocationmanageruserlocation

Trying to get user location using SwiftUI in the content view but showing nil


I’m a beginner, trying to get the latitude and longitude from LocationManager and show it in the ContentView using SwiftUI but result showing nil. But the console prints the data from LocationManager class. Not showing the latitude and longitude from the content view. Can anyone help? (Xcode 11)

This is the LocationManager class

import Foundation
import CoreLocation
import Combine

class LocationManager: NSObject,CLLocationManagerDelegate, ObservableObject {
private let manager: CLLocationManager
var willChange = PassthroughSubject<LocationManager, Never>()

var getLat: String = ""
var getLon: String = ""

var lastKnownLocation: CLLocation? {
    willSet {
        willChange.send(self)
    }
}

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    if status == .denied {
        print("denied")
    }
    else{
        print("athorized")
        manager.requestLocation()
    }
}

func start() {
    manager.requestWhenInUseAuthorization()
    manager.startUpdatingLocation()
}

init(manager: CLLocationManager = CLLocationManager()) {
    self.manager = manager
    super.init()
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    print(error.localizedDescription)
}

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

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    //print(locations)
    lastKnownLocation = locations.last
    
    getLat = "\(locations.last!.coordinate.latitude)"
    getLon = "\(locations.last!.coordinate.longitude)"
    
    showLocation()
}

func showLocation() {
    print("from showLocation")
    print("Latitude: \(getLat)")
    print("Longitude: \(getLon)")
}

}

This is the console that shows the latitude and longitude from LocationManager class

This is the Content View

import SwiftUI
import CoreLocation

struct ContentView: View {
    @State var managerDelegate = LocationManager()
    @State var manager = CLLocationManager()
    @ObservedObject var location = LocationManager()

    var lat: String {
        return "\(location.lastKnownLocation?.coordinate.latitude ?? 0.0)"
    }

    var lon: String {
        return "\(location.lastKnownLocation?.coordinate.longitude ?? 0.0)"
    }

    init() {
        self.manager.delegate = self.managerDelegate
        do {
            try self.manager.requestAlwaysAuthorization()
        }
        catch {
            print(error.localizedDescription)
            self.manager.requestAlwaysAuthorization()
        }
    }

    var body: some View {
        VStack {
            Text("Hello, World")
            Text("Latitude: \(lat)")
            Text("Longitude: \(lon)")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

This is output of ContentView in the Simulator


Solution

  • Alright, I found myself the time to read your code and I have a solution for you.

    First, you'll want to set the delegate to the variable that you are observing, inside ContentView.init():

    self.manager.delegate = self.location
    

    Immediately after, you'll want to call the startUpdating() function that you defined in LocationManager:

    self.location.startUpdating()
    

    Finally, you may want to remove your will-change implementation and opt for the more simple:

    @Published var lastKnownLocation: CLLocation?
    

    The published wrapper will automatically trigger object changes for you.


    Refactor

    Now if you like, you can also reduce your init statement down to one line:

    init() {
        self.location.startUpdating()
    }
    

    Which means you can freely remove a couple properties:

    `@State var managerDelegate = LocationManager()`
    
    `@State var manager = CLLocationManager()`
    

    TLDR; Here is a shortened version of the code:

    The content view

    import SwiftUI
    
    struct ContentView: View {
        @ObservedObject var location = LocationManager()
    
        var lat: String {
            return "\(location.lastKnownLocation?.coordinate.latitude ?? 0.0)"
        }
    
        var lon: String {
            return "\(location.lastKnownLocation?.coordinate.longitude ?? 0.0)"
        }
    
        init() {
            self.location.startUpdating()
        }
    
        var body: some View {
            VStack {
                Text("Location breakdown")
                Text("Latitude: \(lat)")
                Text("Longitude: \(lon)")
            }
            .padding()
            .padding()
            .background(Color.white)
            .cornerRadius(10)
            .shadow(radius: 10)
        }
    }
    

    The Location Manager

    import Foundation
    import CoreLocation
    
    class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {
        private let manager = CLLocationManager()
        @Published var lastKnownLocation: CLLocation?
        
        func startUpdating() {
            self.manager.delegate = self
            self.manager.requestWhenInUseAuthorization()
            self.manager.startUpdatingLocation()
        }
        
        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
            lastKnownLocation = locations.last
            
        }
        
    }