Home > Mobile >  Cant Update CLLocation() from user Location SwiftUi
Cant Update CLLocation() from user Location SwiftUi

Time:11-10

Hello I’m really new to iOS and SwiftUi so don't hate me if my formatting is wrong haha. I’m having trouble converting the users location coordinates to a US address. I have gotten most of the code done but for some reason on line 109 I create the @Published var location and set it to some random location and then I try to update it on line l72 but for some reason it just uses the first location I pass in on line 109. Id love for this to update and use the users current location after they grant permission. Thanks you for any suggestions you have :)

//
//  ContentView.swift
//  Parked
//
//  Created by Luke Jamison on 11/2/21.
//

import SwiftUI
import MapKit
import CoreLocation
import CoreLocationUI

struct ContentView: View {
    static let sharedV = ContentView()
    @StateObject private var viewModel = ContentViewModel()
    
    
    @State var addressData: String = ""
    @State private var showingImagePicker = false
    @State private var image: Image?
    @State private var inputImage: UIImage?
    //@State var geo: Any = locationManager.location
    var body: some View {
        NavigationView {
            VStack {
                Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
                    .onAppear {
                        viewModel.checkIFLocationServicesisEnabled()
                        setData()
                        //viewModel.runSetDatafunc()
                    }.frame(maxWidth: .infinity, maxHeight: .infinity).cornerRadius(20).padding()
                
                Form {
                    Section(header: Text("Add Location Details")) {
                        Text(addressData)
                        Menu {
                            Button(action: {
                                self.showingImagePicker = true
                            }) {
                                Label("Take Picture", systemImage: "camera.shutter.button")
                            }
                            image?.resizable()
                                .frame(width: 250, height: 250)
                                .clipShape(Circle())
                                .overlay(Circle().stroke(Color.white, lineWidth: 4))
                                .shadow(radius: 10)
                            
                            
                        } label: {
                            Label("Add Image", systemImage: "photo.on.rectangle.angled")
                        }
                        
                    }
                    if image != nil {
                        Section(header: Text("Image Taken")) {
                            ZStack {
                                Rectangle().fill(Color.secondary)
                                
                                if image != nil {
                                    image?.resizable().scaledToFit()
                                } else {
                                    
                                }
                            }
                            Button(action: {
                                image = nil
                            }) {
                                Label("Remove Picture", systemImage: "trash.fill")
                            }
                        }
                    } else {
                        
                    }
                }.sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
                    ImagePicker(image: self.$inputImage)
                    
                }.navigationBarTitle("Parked", displayMode: .automatic)
            }
        }.navigationViewStyle(.stack)
    }
    
    func loadImage() {
        guard let inputImage = inputImage else { return }
        image = Image(uiImage: inputImage)
        
    }
    func setData() {
        ContentViewModel.shared.resolveLoactionName(with: viewModel.location) { [self] locationName in
            self.addressData = locationName
        }
    }
    
    
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
        static let shared = ContentViewModel()
        
        @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33.4, longitude: -117.4), span:
                                                    MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
        
        
        @Published var address = ""
        @Published var location = CLLocation.init(latitude: 33, longitude: -117)
        
        var locationManager: CLLocationManager?
        
        func checkIFLocationServicesisEnabled() {
            if CLLocationManager.locationServicesEnabled() {
                locationManager = CLLocationManager()
                locationManager!.delegate = self
                //locationManager?.desiredAccuracy = kCLLocationAccuracyBest
            } else {
                print("this is off turn in the settings...")
            }
        }
        
        public func resolveLoactionName(with location: CLLocation, completion: @escaping ((String) -> Void)) {
            
            let geocoder = CLGeocoder()
            geocoder.reverseGeocodeLocation(location, preferredLocale: .current) { placemarks, error in
                guard let place = placemarks?.first, error == nil else {
                    completion("")
                    return
                }
                print(place)
                var name = ""
                
                if let locality = place.subThoroughfare {
                    name  = locality
                }
                
                if let street = place.thoroughfare {
                    name  = " \(street)"
                }
                
                if let city = place.locality {
                    name  = " \(city)"
                }
                
                if let adminRegion = place.administrativeArea {
                    name  = ", \(adminRegion)"
                }
                
                if let zipCode = place.postalCode {
                    name  = " \(zipCode)"
                }
                
                completion(name)
            }
        }
        
         private func checkLocationAuth() {
            guard let locationManager = locationManager else { return }
            
            switch locationManager.authorizationStatus {
                
            case .notDetermined:
                locationManager.requestWhenInUseAuthorization()
            case .restricted:
                print("location is restricted")
            case .denied:
                print("location is denied in settings for app")
            case .authorizedAlways, .authorizedWhenInUse:
                region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
                ContentView.sharedV.setData()
                self.location = CLLocation(latitude: region.self.center.latitude, longitude: region.self.center.longitude)
                print(location)
                //runSetDatafunc()
                //address = "\(locationManager.location!.coordinate.latitude), \(locationManager.location!.coordinate.longitude)"
                /*geocode(latitude: region.center.latitude, longitude: region.center.longitude) { placemark, error in
                    let placemark = placemark?.first
                    //print(placemark?.description)
                    //self.address = "\(placemark?.thoroughfare), \(placemark?.locality), \(placemark?.administrativeArea)"
                }*/
            @unknown default:
                break
            }
            
        }
        public func runSetDatafunc() {
           
        }
        func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
            checkLocationAuth()
            
        }
        
    }
    
}

CodePudding user response:

Unfortunately, due to the transient nature of SwiftUI views, you definitely won't have luck making a singleton out of a View. But, you don't need it because you're already publishing your region and location, which your View will update in reaction to.

Here's a simplified/refactored version of your code:


struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Text(viewModel.address)
            Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
                .onAppear {
                    viewModel.checkIfLocationServicesisEnabled()
                }
        }
    }
}

final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
    static let shared = ContentViewModel()
    
    @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33.4, longitude: -117.4), span:
                                                MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
    @Published var address = ""
    @Published var location = CLLocation.init(latitude: 33, longitude: -117)
    
    private var lastResolved : CLLocation = .init()
    
    var locationManager: CLLocationManager?
    
    func checkIfLocationServicesisEnabled() {
        if CLLocationManager.locationServicesEnabled() {
            locationManager = CLLocationManager()
            locationManager?.startUpdatingLocation()
            locationManager?.delegate = self
            //locationManager?.desiredAccuracy = kCLLocationAccuracyBest
        } else {
            print("this is off turn in the settings...")
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let last = locations.last else { return }
        
        if last.distance(from: lastResolved) > 200 {
            resolveLocationName(with: last) { address in
                self.address = address
                self.lastResolved = last
            }
        }
        
        self.location = last
    }
    
    public func resolveLocationName(with location: CLLocation, completion: @escaping ((String) -> Void)) {
        
        let geocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(location, preferredLocale: .current) { placemarks, error in
            guard let place = placemarks?.first, error == nil else {
                completion("")
                return
            }
            print(place)
            var name = ""
            
            if let locality = place.subThoroughfare {
                name  = locality
            }
            
            if let street = place.thoroughfare {
                name  = " \(street)"
            }
            
            if let city = place.locality {
                name  = " \(city)"
            }
            
            if let adminRegion = place.administrativeArea {
                name  = ", \(adminRegion)"
            }
            
            if let zipCode = place.postalCode {
                name  = " \(zipCode)"
            }
            
            completion(name)
        }
    }
    
    private func checkLocationAuth() {
        guard let locationManager = locationManager else { return }
        
        switch locationManager.authorizationStatus {
            
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
        case .restricted:
            print("location is restricted")
        case .denied:
            print("location is denied in settings for app")
        case .authorizedAlways, .authorizedWhenInUse:
            region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
            self.location = CLLocation(latitude: region.self.center.latitude, longitude: region.self.center.longitude)
            print(location)
        @unknown default:
            break
        }
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        checkLocationAuth()
    }
}

Note that I'm calling startUpdatingLocation() and implementing didUpdateLocations -- these will allow you to get/update the user's location.

In the simulator, you can use the Features->Location menu to simulate different spots. You'll see it resolve different spots and show the address at the top of the screen.

  • Related