2

I'm new to Swift development so if I've done something especially stupid, please let me know and I'll just delete the question.

I have a view that contains a map. On tap, I want to show an alert with the option to confirm a particular action.

As-is, attempting to show the alert results in:

Attempt to present X on Y whose view is not in the window hierarchy!

This is a work in progress and is nowhere near complete. Here is my current code:

import SwiftUI
import MapKit
import UIKit
import GoogleMaps
import GooglePlaces
import CoreLocation

final class MapsView: UIViewController, UIViewRepresentable, ObservableObject, GMSMapViewDelegate {

    @Published var visible = false
    @Published var coordinates:CLLocationCoordinate2D? = nil
    let marker : GMSMarker = GMSMarker()
    let locationManager = CLLocationManager()
    var address = ""

    init() {

        // Get location permission.
        locationManager.requestAlwaysAuthorization()

        // Start getting location information.
        if( CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
                CLLocationManager.authorizationStatus() ==  .authorizedAlways){

            // Start monitoring location.
            locationManager.startUpdatingLocation()
        }else{
            // Don't show the view if we don't have permission.
            // TODO: Its probably a good idea to give some feedback here.
            visible = false
        }

        super.init(nibName:nil, bundle:nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {

        // Drop a marker where the user tapped.
        // Confirm checkin.

        // Remove existing markers.
        mapView.clear()

        coordinates = coordinate

        showConfirm()

    }

    /// Creates a `UIView` instance to be presented.
    func makeUIView(context: MapsView.Context) -> GMSMapView {

        // Get lat/lon
        let location = locationManager.location
        let coords = location?.coordinate
        let lat = coords?.latitude
        let lon = coords?.longitude

        // Set camera position.
        let camera = GMSCameraPosition.camera(withLatitude: lat!, longitude: lon!, zoom: 17.0)
        let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)

        // Enable "my location"
        mapView.isMyLocationEnabled = true

        // Set the delegate.
        mapView.delegate = self

        return mapView
    }

    func updateUIView(_ mapView: GMSMapView, context: MapsView.Context) {

        // TODO

    }

    func showConfirm() {

        if coordinates != nil {
            // Get the address.
            let gc = GMSGeocoder()
            gc.reverseGeocodeCoordinate(coordinates!) { (response, error) in

                 // Get the first address result.
                 let result = response?.firstResult()

                 // Build the address string.
                 self.address = ""
                 for line in (result?.lines)! {
                     self.address += " " + line
                 }

                 // Create an alert.
                 let alert = UIAlertController(title: "Confirm Checkin", message: "Check in to \(self.address)?", preferredStyle: UIAlertController.Style.alert)

                 // Add NO button.
                 alert.addAction(UIAlertAction(title: "NO", style: UIAlertAction.Style.cancel, handler: nil))

                 // Add YES button.
                 alert.addAction(UIAlertAction(title: "YES", style: UIAlertAction.Style.default))

                 // Show the alert.
                self.present(alert, animated: true, completion: nil)

             }
        }
    }

}

From the following, how can I show an Alert without this error?

// Create an alert.
let alert = UIAlertController(title: "Confirm Checkin", message: "Check in to \(self.address)?", preferredStyle: UIAlertController.Style.alert)

// Add NO button.
alert.addAction(UIAlertAction(title: "NO", style: UIAlertAction.Style.cancel, handler: nil))

// Add YES button.
alert.addAction(UIAlertAction(title: "YES", style: UIAlertAction.Style.default, handler: nil))

// This line produces the error
self.present(alert, animated: true, completion: nil)

I'm aware of this question, but I don't have a viewDidAppear method and I'm not sure how to properly move my logic into one, as I want this alert displayed when the map is touched, rather than when the view appears.

That1Guy
  • 7,075
  • 4
  • 47
  • 59
  • 1
    I recommend not showing the alert from ViewModel , this is a bad practice , try rather than that to call it from the VC. – SafoineMoncefAmine Dec 26 '19 at 18:34
  • You cannot say `self.present` if self is not the current view controller. You need to figure out what view controller’s view is showing and send `present` to that view controller. – matt Dec 26 '19 at 21:38
  • Thanks for the advice @matt . Would you consider providing an answer? Although what you said makes sense, I'm so new to Swift I don't even know where to begin with that. – That1Guy Dec 27 '19 at 13:45
  • Sorry, what I have said is obvious and you have not provided sufficient information for me to say more. If this is a SwiftUI app there are no UIAlertControllers or view controllers so what you’re doing is all very mysterious to me. – matt Dec 27 '19 at 16:45
  • @matt lol fair enough. Thanks anyway. I'll give it my best and update my question late on if I have a more cohesive thought – That1Guy Dec 27 '19 at 19:30

0 Answers0