Luke
Luke

Reputation: 125

Cant Update CLLocation() from user Location SwiftUi

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

Upvotes: 0

Views: 810

Answers (1)

jnpdx
jnpdx

Reputation: 52555

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.

Upvotes: 2

Related Questions