surnan
surnan

Reputation: 99

Unable to move alertController to UIViewController extension, if it requires handler function

Throughout my program, I call UIAlertControllers multiple times. To make things easier to read, I was able to successfully factor out AlertController if it only has an "OK" action with nil as the handler.

I am struggling to do the same thing if I want to pass a function that I want to link as UIAlertAction. func showOKCancelAlertController does not compile

showOKAlertController(title: "Network Error", message: "Unable to download photos") //This is an example of how I call Alert Controller throughout my code.

extension UIViewController {
    func showOKAlertController(title: String, message: String){
        let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        myAlertController.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        present(myAlertController, animated: true)
    }

    func showOKCancelAlertController(title: String, message: String, okFunction: UIAlertAction ){
        let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        myAlertController.addAction(UIAlertAction(title: "OK", style: .default, handler: okFunction))
        myAlertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
        present(myAlertController, animated: true)
    }
} 

Upvotes: 1

Views: 101

Answers (4)

Joshpy
Joshpy

Reputation: 600

Improve your code to my way

This is flexible to deal the completion handler

extension UIViewController {
    func showOKAlertController(title: String, message: String? = nil, okCompletion: @escaping (() -> ()) = {}, presentCompletion: @escaping (() -> ()) = {}) {
        let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default) { (action: UIAlertAction) in
            okCompletion()
        }
        myAlertController.addAction(okAction)
        DispatchQueue.main.async {
            self.present(alertController, animated: true) {
                presentCompletion()
            }
        }
    }

    func showOKCancelAlertController(title: String, message: String? = nil, okCompletion: @escaping (() -> ()) = {}, cancelCompletion: @escaping (() -> ()) = {}, presentCompletion: @escaping (() -> ()) = {}) {
        let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default) { (action: UIAlertAction) in
            okCompletion()
        }
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (cancel: UIAlertAction) in
            cancelCompletion()
        }
        myAlertController.addAction(okAction)
        myAlertController.addAction(cancelAction)
        DispatchQueue.main.async {
            self.present(alertController, animated: true) {
                presentCompletion()
            }
        }
    }
}

showOKAlertController(title: "message")

or

showOKAlertController(title: "message", message: "message")

or

showOKCancelAlertController(title: "message", message: nil, okCompletion: {
    // ok completion handling
}) {
    // present completion handling
}

or you can refer above useful from my gist

Upvotes: 1

Ahmad F
Ahmad F

Reputation: 31695

Clearly, the error that you are facing is:

Cannot convert value of type 'UIAlertAction' to expected argument type '((UIAlertAction) -> Void)?'

that's because okFunction parameter of type UIAlertAction, this is the incorrect part. You should let okFunction to be of type ((UIAlertAction) -> Void)? instead:

func showOKCancelAlertController(title: String, message: String, okFunction: ((UIAlertAction) -> Void)?) {
    let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
    myAlertController.addAction(UIAlertAction(title: "OK", style: .default, handler: okFunction))
    myAlertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
    present(myAlertController, animated: true)
}

and that's because the UIAlertAction init signature is:

init(title: String?, style: UIAlertAction.Style, handler: ((UIAlertAction) -> Void)? = nil),

the handler parameter expects ((UIAlertAction) -> Void)?.

Therefore, you call it as:

showOKCancelAlertController(title: "title", message: "message") { _ in
    print("here is what to do when tapping the OK button")
}

Also, in case there is no action for the OK button, what you could is to give a default nil value for okFunction parameter:

func showOKCancelAlertController(title: String, message: String, okFunction: ((UIAlertAction) -> Void)? = nil)

and calling it as:

showOKCancelAlertController(title: "title", message: "message")

Actually, this leads to a pretty cool thing to your case which is: at this point you don't even need to implement two different methods, you could just implement one method and pass the okFunction parameter for it only if it's needed! Example:

func showAlertController(title: String, message: String, okFunction: ((UIAlertAction) -> Void)? = nil) {
    let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
    myAlertController.addAction(UIAlertAction(title: "OK", style: .default, handler: okFunction))
    if let okFun = okFunction {
        myAlertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: okFun))
    }

    present(myAlertController, animated: true)
}

If you want to show it with one button:

showAlertController(title: "title", message: "message")

and for two buttons:

showAlertController(title: "title", message: "message") { _ in
    // ...
}

Upvotes: 1

Shehata Gamal
Shehata Gamal

Reputation: 100549

Could be

extension UIViewController { 
    func showOKAlertController(title: String, message: String,ok:@escaping(() -> ())) {
        let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
        myAlertController.addAction(UIAlertAction(title: "ok", style: .default, handler: { (al) in
            ok()
        }))
        myAlertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
        present(myAlertController, animated: true)
    }
}

showOKAlertController(title: "This is title", message: "this is message") {
   print("ok")
}

Upvotes: 1

Razi Tiwana
Razi Tiwana

Reputation: 1435

The handler expected type is ((UIAlertAction) -> Swift.Void)? not UIAlertAction

Update your code to

func showOKCancelAlertController(title: String, message: String, okFunction: ((UIAlertAction) -> Swift.Void)? ){
     let myAlertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
     myAlertController.addAction(UIAlertAction(title: "OK", style: .default, handler: okFunction))
     myAlertController.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
     present(myAlertController, animated: true)
}

Upvotes: 1

Related Questions