Search code examples
iosswiftcllocationmanagergoogle-maps-sdk-iosgmsmapview

mapView: GMSMapView! nil causing the app to crash (swift)


I'm completely new when it comes to use Google Maps SKD for iOS and I have the following problem: My app uses Firebase and the MapVC is the first VC if the user is logged in. I set a UIVIew in the storyboard with the size I wanted and made its class GMSMapView. I also use the CLLocation Manager and the location comes ok, I pass it on to the camera variable and it's fine. But if add this code: mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera) or this code: self.mapView.camera = camera the app crashes because mapView is nil. If I don't add those lines of code, the map shown is just the default map (Europe). What I need is the map camera to show the location I'm getting from CLLocation and updates it on the map as the location changes as well.

My code for the MapVC is below:

import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation
import Alamofire
import Firebase
import FirebaseFirestore

class AlertaVC: UIViewController, CLLocationManagerDelegate {

    @IBOutlet weak var alertaBtn: RoundButton!
    var latitude: CLLocationSpeed?
    var longitude: CLLocationDegrees?
    var mapView: GoogleMaps.GMSMapView!
    var db: Firestore!
    var userID: String?
    var nameUser: Any?


    let locationManager = CLLocationManager()

    @IBAction func dispararAlertaBtn(_ sender: UIButton) {
        //changing the title and color after the alert is sent
        alertaBtn.isSelected = !alertaBtn.isSelected

        if alertaBtn.isSelected {

            //setting up twilio to send the alert SMS
            let headers: HTTPHeaders = [
                "Content-Type": "application/x-www-form-urlencoded"
            ]
            //add the phone numbers from the firebase document
            let parameters: Parameters = [
                "To": "phone-number",
                "Body": "Ola, to enviando SMS!"
            ]
            //change the SMS body to include the user's name and the link with live location
            AF.request("https://url.twil.io/smsAlerta", method: .post, parameters: parameters, headers: headers).responseJSON { response in
                print(response.response as Any, "response alamofire")
            }

            alertaBtn.setTitle("Encerrar alerta!", for: .normal)
            alertaBtn.backgroundColor = UIColor(red: 0.83529, green: 0.4, blue: 0.5725490196, alpha: 1.0)
        } else {
            alertaBtn.setTitle("Disparar alerta!", for: .normal)
            alertaBtn.backgroundColor = UIColor(red: 0.3254, green: 0.1921, blue: 0.2627, alpha: 1.0)
        }

    }


    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.requestAlwaysAuthorization()
        locationManager.requestWhenInUseAuthorization()
        if CLLocationManager.locationServicesEnabled() {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
            locationManager.startUpdatingLocation()
        }
        db = Firestore.firestore()

        let user = Auth.auth().currentUser
        if let user = user {
          // The user's ID, unique to the Firebase project.
            self.userID = user.uid
            print(self.userID!, "user ID firebase")
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        //showCurrentLocation() Tried calling the function @ViewWillAppear but it didn't work either
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
        latitude = locValue.latitude
        longitude = locValue.longitude

        let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: latitude ?? 19.741755, longitude: longitude ?? -155.844437, zoom: 7.0) //if doesnt load will show Hawaii
        print(camera as Any, "camera") <-- WORKS FINE: GMSCameraPosition 0x600000f389f0: target:(37.332, -122.031) bearing:0.000 zoomLevel:7.000 viewingAngle:0.000 camera
        self.mapView.camera = camera <<--- CRASHES HERE: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

        self.mapView?.animate(to: camera) <<-- WITHOUT THE LINE ABOVE THIS ANIMATION DOESN`T HAPPEN, BECAUSE MAPVIEW IS NIL

        print("locations = \(latitude ?? 56.56), \(longitude ?? 45.45)")
    }


    func showCurrentLocation() { <<- NOT BEING CALLED, BUT IF IT IS, CRASHES
        //mapView.settings.myLocationButton = true
        let locationObj = locationManager.location!
        let coord = locationObj.coordinate
        let lattitude = coord.latitude
        let longitude = coord.longitude
        print(" lat in  updating \(lattitude) ")
        print(" long in  updating \(longitude)")

        let center = CLLocationCoordinate2D(latitude: locationObj.coordinate.latitude, longitude: locationObj.coordinate.longitude)
        let marker = GMSMarker()
        marker.position = center
        marker.title = "current location"
        marker.map = mapView
        let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: lattitude, longitude: longitude, zoom: 7.0)
        mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera) <-- CRASHES HERE
        self.mapView.animate(to: camera)
    }

    func fetchUserName(userId: String) {
        db.collection("Usuarias").document(self.userID!).getDocument() {
            (document, err) in
           if let document = document, document.exists {
            let dataDescription = document.data()!["Nome"] ?? "nil"
                print("Document data: \(dataDescription)")
            self.nameUser = dataDescription
            print(self.nameUser as Any, "dentro do fetch")
            } else {
                print("Document does not exist")
            }
        }
    }

}

I'd appreciate any help. Thanks =)


Solution

  • If you've created the 'View' in storyboard and assign as 'GMSMapView' you should add it as 'IBOutlet' in ViewController, you can do it by clicking 'Option' on keyboard and drag from map to ViewController like you've add 'alertaBtn', if you want to create map programmatically you need to initiate it in 'viewDidLoad' method then use it to set camera or to do other changes.