2

I'm trying to get more into the Swift Closure concept and I have this problem, I have the following UIAlertController method:

 public static func showSerialNumberAlertController(handler: @escaping (UIAlertAction) -> String?) {
    let alertController = UIAlertController(title: "Serial Number", message: "Please enter your serial number", preferredStyle: .alert)

    // ADD ACTIONS HANDLER
    let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
    cancelAction.isEnabled = true
    alertController.addAction(cancelAction)

    let continueAction = UIAlertAction(title: "Continue", style: .default) { (_) in
        let serialNumberField = alertController.textFields![0] as UITextField
        let text = serialNumberField.text
        print("Serial number entered: \(text!)")
    }
    continueAction.isEnabled = false
    alertController.addAction(continueAction)

    // ADD TEXT FIELDS
    alertController.addTextField { (textField) in
        textField.placeholder = "Serial number"
        // enable continue button when serial not empty
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextFieldTextDidChange, object: textField, queue: OperationQueue.main) { (notification) in
            continueAction.isEnabled = textField.text != "" && textField.text?.count as! Int > 3
        }
    }

    alertController.view.tintColor = Colors.mainColor
    getRootViewController()?.present(alertController, animated: true)
}

now what I'm trying to do is to replace the handler of the continueAction UIAlertAction with a handler I receive from outside which will extract the text from the serial number UITextField when the user presses the continue button and pass it as the returned value.

So I have this method outside of this scope in another file, from which I'm calling this static method:

public func getSerialFromAlert(alertController: UIAlertController) -> String? 
{
    let serialNumberField = alertController.textFields![0] as UITextField
    let text = serialNumberField.text
    print("Serial number entered: \(text!)")
    return text
}

I want to pass this method as a handler but when I do that I get this error:

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

So the question is: Is it possible to achieve this and pass a handler that will return a value as a parameter? If so what would be the right way to do this? If not, what would be the alternative for an UIAlertController action delegate that has to access alert views in order to extract data?

Emil Adz
  • 40,709
  • 36
  • 140
  • 187
  • Under what class are you writing showSerialNumberAlertController? And under what class are you calling getSerialFromAlert? – El Tomato Feb 25 '18 at 07:46
  • @ElTomato, showSerialNumberAlertController is written under Utils class, while getSerialFromAlert is under MainViewController I want to call showSerialNumberAlertController from MainViewController and pass getSerialFromAlert as a parameter that will return me a value when continue button is pressed. – Emil Adz Feb 25 '18 at 08:08
  • Then what you should do is to include a view controller in the showSerialNumberAlertController function as an argument. – El Tomato Feb 25 '18 at 08:13
  • You say "a closure to UIAlertAction that will return a String." But you don't have a string in your escape clause. – El Tomato Feb 25 '18 at 08:28

1 Answers1

3

If I understood you correctly you could change your method signature to

showSerialNumberAlertController(completion: @escaping (String) -> Void)

and within this function change the continue action to:

let continueAction = UIAlertAction(title: "Continue", style: .default) { (_) in
    let serialNumberField = alertController.textFields![0] as UITextField
    let text = serialNumberField.text
    completion(text ?? "")
}

Finally you call the method like this:

UIAlertController.showSerialNumberAlertController { serial in
    print("Serial number entered: \(serial)")
}
André Slotta
  • 13,774
  • 2
  • 22
  • 34
  • what "completion(text ?? "")" means? because I'm getting "Use of unresolved identifier 'completion'" error for this line? – Emil Adz Feb 25 '18 at 08:09
  • `serialNumberField.text` returns an optional. So `text` could possibly be `nil`. This line means that the `completion` handler either returns the serial number (if not `nil`) or an empty string. Your problem has to be somewhere else. Did you make all three changes in your code? – André Slotta Feb 25 '18 at 08:11
  • Side note: typecasting seems to be redundant here, you could just use `let text = alertController.textFields?[0].text` instead. – Tomasz Pe Feb 25 '18 at 08:46
  • Yes, you were right. I didn't commit all the changes you made. Now it works, thank you very much for you help. – Emil Adz Feb 25 '18 at 11:40