Can't give parameter values to extension method UIAlertController - swift

I am trying to write an extension method that generates an UIAlertController based on what you give as a parameter.
extension UIAlertController {
func generate(messageText: String, messageTitle: String, buttonText: String) {
let alert = UIAlertController(title: messageTitle, message: messageText, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: buttonText, style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
}
This doesn't give an error.
But when I try to call it and add the parameter values
var alertTest = UIAlertController.generate("")
It gives the following error: Type 'UIAlertController' does not conform to protocol 'StringLiteralConvertible'
How can I fix this?
Or isn't it possible what i am trying to achieve?

There are some differences between how you have defined the generate method in the extension and how you are using it.
generate is declared as a function accepting 3 string parameters and returning void.
You are calling as a static method instead, passing one parameter, and expecting a return value.
The right way of using it is like this:
var alert = UIAlertController()
alert.generate("A Message", messageTitle: "A title", buttonText: "A button label")
However I think that the method implementation is incorrect, because you are creating a new instance of UIAlertController instead of (re)using the one the method is called on. What you probably need is a static method, in which case it should look like:
extension UIAlertController {
class func generate(# parent: UIViewController, messageText: String, messageTitle: String, buttonText: String) -> UIAlertController {
let alert = UIAlertController(title: messageTitle, message: messageText, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: buttonText, style: UIAlertActionStyle.Default, handler: nil))
parent.presentViewController(alert, animated: true, completion: nil)
return alert
}
}
and used as:
var alert = UIAlertController.generate(parent: self, messageText: "A Message", messageTitle: "A title", buttonText: "A button label")

Related

Swift: How to call two times the same alert from another alert?

I have simplified my problem to the following code where the same behavior occurs.
What I want to do is calling a method which includes an alert (messageWindow() in my code) two times which means one behind the other. I want to call that method from another method which also includes an alert (userInput()) in my code.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
userInput()
}
func userInput() {
let alert = UIAlertController(
title: "Welcome",
message: "Do you want say hello?",
preferredStyle: .alert)
let actionYes = UIAlertAction(
title: "Yes",
style: .default) {_ in
print("hello")
self.messageWindow(title: "1st call", message: "Hello!")
self.messageWindow(title: "2nd call", message: "Hello!!")
}
let actionNo = UIAlertAction(
title: "No",
style: .default) { (action) in }
alert.addAction(actionYes)
alert.addAction(actionNo)
self.present(alert, animated: true)
}
func messageWindow (title: String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: .alert)
let actionOk = UIAlertAction(title: "OK", style: .default) { (action) in }
alert.addAction(actionOk)
self.present(alert, animated: true)
}
}
My problem is that the second call won't be executed (code snippet below). That means I don't see a window popping up like in the first call.
self.messageWindow(title: "2nd call", message: "Hello!!")
I'm relative new in coding with Swift. Please excuse my question in case it is a really simple one. I didn't found anything which helped me solving this problem.
I appreciate your help.
Thanks.

Pass "function with parameter" as parameter

This doesn't compile
func showAlert(_ title: String, message: String,
onOk: (()->())? = nil,
onAnotherAction:((anotherActionTitle : String)-> Void)? = nil) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default) { (action) in
onOk?()
}
let anotherAction = UIAlertAction(title: anotherActionTitle, style: .default) { (action) in
onAnotherAction?()
}
alertController.addAction(ok)
alertController.addAction(anotherAction)
...
}
This compiles
func showAlert(_ title: String, message: String,
onOk: (()->())? = nil,
onAnotherAction:((String)-> Void)? = nil)
However, I have to declare another parameter for the title anotherActionTitle of onAnotherAction().
Is there a way make the first approach work? Thanks!
However, I have to declare another parameter for the title anotherActionTitle of onAnotherAction()
No, you don't have to do that. Just make it a normal parameter of the function as a whole:
func showAlert(_ title: String, message: String,
onOk: (()->())? = nil,
anotherActionTitle: String? = nil,
onAnotherAction: (()->())? = nil) {
The rest of your function will then compile and work correctly.
Since the implementation of SE-0111 as part of Swift 3, it is no longer possible to have named parameters for closure types.
There is a conceptual roadmap that the Swift core team has laid out for restoring named closure parameters at some point in the future, but no timeline for implementation that I am aware of:
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160711/024331.html

Swift creating a function that runs another function that was implemented while called.(Not that complex)

Hello i am trying to create a kickass function to show alerts and run it's function. Buuut unfortunately Xcode and i am getting confused in here:
buttonAction:Array<(Any) -> Any)>
Expected '>' to complete generic argument list
func callAlert(_ view: UIViewController, title:String, message:String, buttonName:Array<String>, buttonAction:Array<(Any) -> Any)>) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
for index in 0..<buttonName.count{
alert.addAction(UIAlertAction(title: buttonName[index], style: .default, handler: { action in
switch action.style{
case .default:
print("default")
buttonAction()
case .cancel:
print("cancel")
case .destructive:
print("destructive")
}}))}
view.present(alert, animated: true, completion: nil)
}
How do i call function? Please check below:
callAlert(self,
title: "Donate type",
message: "Thanks for your support!",
buttonName: ["Buy me a coffee!","Something"]
)
First of all I highly recommend to implement the method as an extension of UIViewController.
Second of all I'd prefer presentAlert() over callAlert()
Third of all rather than two arrays for buttons and actions use one array of tuples for title, style and action.
By the way unspecified type (Any) -> Any is very, very bad because UIAlertAction handlers are clearly ((UIAlertAction) -> Void)?
Finally add an optional completion handler
extension UIViewController {
func presentAlert(title: String,
message: String,
alertActions: [(title: String, style: UIAlertAction.Style, action: ((UIAlertAction) -> Void)?)],
completion: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
for action in alertActions {
alert.addAction(UIAlertAction(title: action.title, style: action.style, handler: action.action))
}
self.present(alert, animated: true, completion: completion)
}
}
And use it inside an UIViewController
let buyCoffeeAction : (UIAlertAction) -> Void = { action in
// do something
}
let somethingAction : (UIAlertAction) -> Void = { action in
// do something
}
presentAlert(title: "Donate type",
message: "Thanks for your support!",
alertActions: [(title: "Buy me a coffee!", style: .default, action: buyCoffeeAction),
(title: "Something", style: .destructive, action: somethingAction)],
completion: nil)

Method using the calling object's "self" by default

I have a utility class containing functions that are used by multiple other classes. One of those is an alert function:
class Utils {
func doAlert (title: String, message: String, target: UIViewController) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
target.present(alert, animated: true, completion: nil)
}
}
This function will always target self on the view controller, so I'd like to not have to add target: self every time I call the function, but I can't just set it as a default value since that causes it to refer back to the Utils class. Is there any way I can rewrite this to avoid that?
Utility classes are an antipattern exactly for this reason, what you really want to use is an extension:
extension UIViewController {
func doAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
and then you can call the method directly on all your controllers:
self.doAlert(title: "title", message: "message")
In general avoid classes with utility methods. Try to add methods to the types to which the functionality actually belongs.
Instead of putting the function in your Utils class, you could put it in an extension to UIViewController, like this:
extension UIViewController {
func doAlert (title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
target.present(alert, animated: true, completion: nil)
}

Swift expecting declaration but already declared

I am trying to create an alert view that performs a certain action when the button is clicked. I have tried creating a new class for the alert view, but when I try to add an action to the alert view controller, Xcode tells me that it is expecting a declaration, although the variable is declared just two steps above the line where the error occurs. Here's the code
class alerts: UIAlertController {
var alertThenGenerateNewThingController: UIAlertController = UIAlertController()
var generateNewThingOkButton = UIAlertAction (title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
println ("generate new thing action")
}
alertThenGenerateNewThingController.addAction (generateNewThingOkButton) // Here is where Xcode says it expected a declaration
func alertThenGenerateNewThing (alertTitle: String, alertMessage: String) {
alertThenGenerateNewThingController = UIAlertController (title: alertTitle, message: alertMessage, preferredStyle: .Alert)
self.presentViewController (alertThenGenerateNewThingController, animated: true, completion: nil)
}
}
You can't subclass UIAlertController, for one thing. Second of all, you can only interact with object properties inside of methods, functions, or the global scope. Run this code inside of the view controller where you plan on presenting your alert:
class viewController: UIViewController {
var alertThenGenerateNewThingController: UIAlertController = UIAlertController()
var generateNewThingOkButton = UIAlertAction (title: "OK", style: UIAlertActionStyle.Default) {
UIAlertAction in
println ("generate new thing action")
}
override func viewDidLoad() {
alertThenGenerateNewThingController.addAction(generateNewThingOkButton)
}
func alertThenGenerateNewThing (alertTitle: String, alertMessage: String) {
alertThenGenerateNewThingController = UIAlertController (title: alertTitle, message: alertMessage, preferredStyle: .Alert)
self.presentViewController (alertThenGenerateNewThingController, animated: true, completion: nil)
}
}