celiux
celiux

Reputation: 623

Swift 3 - store the User Location and call it from different View Controllers

I'm pretty new in programming and this is my first app, so sorry if the approach is very shabby.

I created a helper method to get the user location, because I need to call it from different view controllers so I thought this was a cleaner way to do it. But I don't know why is not working now (no errors, it just show the general view of Europe). But when it was inside the view controller it worked perfectly fine.

I got this new approach from the course I'm doing and I've been researching in many sources. I've also checked this question but I didn't find any solution yet.

Here is the method I created in the GMSClient file. It will get the user location, but if the user disables this option, it will show the default position (centred in Berlin):

    extension GMSClient: CLLocationManagerDelegate {

    //MARK: Initial Location: Berlin

    func setDefaultInitialLocation(_ map: GMSMapView) {
        let camera = GMSCameraPosition.camera(withLatitude: 52.520736, longitude: 13.409423, zoom: 8)
        map.camera = camera

        let initialLocation = CLLocationCoordinate2DMake(52.520736, 13.409423)
        let marker = GMSMarker(position: initialLocation)
        marker.title = "Berlin"
        marker.map = map

    }

    //MARK: Get user location

    func getUserLocation(_ map: GMSMapView,_ locationManager: CLLocationManager) {

        var userLocation: String?

        locationManager.requestWhenInUseAuthorization()

        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {

            if status == .authorizedWhenInUse {
                locationManager.startUpdatingLocation()

                map.isMyLocationEnabled = true
                map.settings.myLocationButton = true

            } else {
                setDefaultInitialLocation(map)
            }
        }

        func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

            if let location = locations.first {

                map.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)

                locationManager.stopUpdatingLocation()

                //Store User Location
                userLocation = "\(location.coordinate.latitude), \(location.coordinate.longitude)"
                print("userLocation is: \((userLocation) ?? "No user Location")")

            }
        }
    }
}

This file has also this singelton:

// MARK: Shared Instance

    class func sharedInstance() -> GMSClient {
        struct Singleton {
            static var sharedInstance = GMSClient()
        }
        return Singleton.sharedInstance
    }

And then I call it in my view controller like this:

class MapViewController: UIViewController, CLLocationManagerDelegate {

    // MARK: Outlets

    @IBOutlet weak var mapView: GMSMapView!


    // MARK: Properties

    let locationManager = CLLocationManager()
    var userLocation: String?
    let locationManagerDelegate = GMSClient()


    // MARK: Life Cycle

    override func viewDidLoad() {

        super.viewDidLoad()

        self.locationManager.delegate = locationManagerDelegate

        GMSClient.sharedInstance().getUserLocation(mapView, locationManager)

    }

Anyone has an idea of what can be wrong?

Thanks!

Upvotes: 1

Views: 913

Answers (2)

celiux
celiux

Reputation: 623

Following what Paulw11 said, I found the faster solution using Notifications.

  1. Send notification from the LocationManager delegate method inside the first view Controller:

    class MapViewController: CLLocationManagerDelegate {
    
        func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    
                    if status == .authorizedWhenInUse {
                        locationManager.startUpdatingLocation()
    
                        mapView.isMyLocationEnabled = true
                        mapView.settings.myLocationButton = true
                    } else {
                        initialLocation()
                    }
                }
    
                func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    
                    if let location = locations.first {
    
                        mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
    
                        locationManager.stopUpdatingLocation()
    
                        let userInfo : NSDictionary = ["location" : location]
    
                        NotificationCenter.default.post(name: NSNotification.Name("UserLocationNotification"), object: self, userInfo: userInfo as [NSObject : AnyObject])
    
                    }
                }
         }
    
  2. Set the second view controller as observer. This way I can store the userLocation and use it later for the search request:

    class NeighbourhoodPickerViewController: UIViewController, UITextFieldDelegate {
    
    var userLocation: String?
    var currentLocation: CLLocation!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        NotificationCenter.default.addObserver(self, selector: #selector(locationUpdateNotification), name: Notification.Name("UserLocationNotification"), object: nil)
    
    }
    
    func locationUpdateNotification(notification: NSNotification) {
        if let userInfo = notification.userInfo?["location"] as? CLLocation {
            self.currentLocation = userInfo
            self.userLocation = "\(userInfo.coordinate.latitude), \(userInfo.coordinate.longitude)"
        }
    }
    

Upvotes: 2

Puneet Sharma
Puneet Sharma

Reputation: 9484

I guess the problem is here,

self.locationManager.delegate = locationManagerDelegate

You have created a new instance of GMSClient, and saved it in the stored property and that instance is set as the delegate property of CLLocationManager.

You need to do this instead,

self.locationManager.delegate = GMSClient.sharedInstance()

You need to do this because you would want singleton instance of GMSClient to be the delegate for CLLocationManager and not a new instance. That way your singleton class would recieve the callbacks from CLLocationManager class.

To understand more about why your code was not working, I would suggest you read more about Objects, Instances, Instance variables, Singletons, Delegate design pattern.

Upvotes: 1

Related Questions