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

1k Views Asked by At

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

1

There are 1 best solutions below

1
Samuel-IH On BEST ANSWER

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
        
    }
    
}