toast
toast

Reputation: 1980

How to get UIViewController in a singleton?

I'm using a singleton class to validate a network response. Inside the function (validResponse()) I'm calling in the singleton, I call another function which pops up an alert box, to let the user know there was an error.

The function inside my singleton class:

func validResponse(data: Data?, response: URLResponse?, error: Error?, viewController: UIViewController, context: String?) -> Bool {
    ...
    DispatchQueue.main.async {
            AlertHelper.showAlertWrapper(viewController: viewController, alertTitle: "Error", alertMessage: self.genericError)
        }
}

The AlertHelper code:

class AlertHelper {
    static func showAlertWrapper(viewController: UIViewController, alertTitle: String, alertMessage: String) {
        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert);

        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil);
        alertController.addAction(okAction);

        viewController.present(alertController, animated: true, completion: nil);
    }
}

Calling validResponse():

let result = self.networkHelper.validResponse(data: data, response: response, error: error, viewController: self, context: "Delete section")

In the above instance self is not going to work, and is just temporarily there until I figure out what to do. I understand I could just pass the relevant UIViewController in viewController, like I did for showAlertWrapper. However this is a bit messy.

Is there some way I can reference the currently present view controller in my singleton class, so that I dont have to pass it in validResponse()?

Upvotes: 0

Views: 1388

Answers (2)

Robin Delaporte
Robin Delaporte

Reputation: 585

You may want to get the top most view controller currently showing in your app. You can do this by retrieving the top most view controller when your singleton wants to display an error.

class AlertHelper {
    static func showAlertWrapper(alertTitle: String, alertMessage: String) {
        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert);

        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil);
        alertController.addAction(okAction);

        if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }

            // viewController should now be your topmost view controller
            viewController.present(alertController, animated: true, completion: nil);
        }
    }
}

And call your showAlertWrapper:

AlertHelper.showAlertWrapper(alertTitle: "Error", alertMessage: self.genericError)

Upvotes: 1

Imotep
Imotep

Reputation: 2056

A workaround and maybe swiftier way would be to have the showAlertWrapperin the UIViewController:

extension UIViewController {
func showAlertWrapper(title: String, message: String) {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert);

        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil);
        alertController.addAction(okAction);

       present(alertController, animated: true, completion: nil);
    }
}

Then you would just do

DispatchQueue.main.async {
            viewController.showAlertWrapper(title: "Error", message: self.genericError)
        }

Upvotes: 1

Related Questions