3

I have been trying to create a UIAlertAtion which also has a handler. I read the answers from this question and know how to do it.

My question is only about the closure portion of it.

1) I know I can write : {alert in println("Foo")} Or {_ in println("Foo")} but I can't write {println("Foo")}. In the comments here it is explained because you need to handle the argument action.

Does this mean that since the handler is of type (UIAlertAction) -> Void)? I must always capture the passed alertAction?


2) I also read this and the answer is basically saying you can pass in a function as your argument, but the function should take something of type UIAlertAction -> Void, which I wrote :

private func anything(action : UIAlertAction) {
    print("hello")
}

and then wrote my alertaction as such:

let anotherAction = UIAlertAction(title: "hi", style: UIAlertActionStyle.Default,
 handler: anything(action)) // error: Use of unresolved identifier 'action'

confused why I get that error


3) In the comments it also said: But in addition to that you don't have to write UIAlertActionStyle.Default in swift. .Default works, too

I tried writing not using the style so it would be defaulted to .Default

let sendLogAction = UIAlertAction(title: "Log") { action in print("goodbye")}

But then I get the following error:

'(title: String, (_) -> ())' (aka '(title: String, _ -> ())') is not convertible to '(title: String?, style: UIAlertActionStyle, handler: ((UIAlertAction) -> Void)?)' (aka '(title: Optional, style: UIAlertActionStyle, handler: Optional ()>)'), tuples have a different number of elements


4) Also reading this answer. I don't understand why we need to pass in alert it makes no sense. It's not like we don't know what are alert's type is...haven't we already defined its type?!! Can anyone explain where passing the action itself would be useful in general, I mean what could we do with it?


I know this is wrote as 4 questions but I think it's really just a foundational question. I have extensively read, used closures/completion handlers in a project I'm working and played in playground but still I'm confused.

Community
  • 1
  • 1
mfaani
  • 33,269
  • 19
  • 164
  • 293
  • #4 makes no sense. What `alert` are you referring to? – rmaddy Nov 30 '16 at 17:00
  • @rmaddy `switch action.style`... he is switching over alert, but hasn't he already defined : `style: .Default` – mfaani Nov 30 '16 at 17:01
  • He is not switching over `alert`. He is switching over `action.style`. Either way, that switch statement is just an example. It's pointless though. No one would do that in real code since you already know what style you set. – rmaddy Nov 30 '16 at 17:03
  • @rmaddy yes, wrong words I am using because sometimes I saw people writing `action in` and sometimes `alert in` which makes no difference... But yet pointless. so for my number 4, I'm asking for a good example of where the captured `action` is used. – mfaani Nov 30 '16 at 17:06

3 Answers3

3
  1. Yes, you must always handle the argument. It's part of the signature and can't be ignored. It's also special Swift syntax being able to drop the handler parameter since it is the last parameter and it is a closure parameter.

  2. Change anything(action) to anything just like in the example you link to.

  3. You misunderstand. They are not saying you can drop the style parameter. They are saying you can drop the UIAlertActionStyle from UIAlertActionStyle.Default meaning you only need to pass .Default as the argument to the style parameter.

  4. You want an example of where the action parameter to the handler is useful. There aren't too many uses really. The only properties are the title, style, and whether it's enabled or not. The latter is pointless because if the handler was called you know it was enabled. The only possible use is if the title or style need to be used in the handler and they weren't hard coded into the alert action when it was created. By accessing the title or style properties in the handler, you have easy access to the actual values used when the action was created.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • 1) drop as in trailing closure? 2) hah! I didn't see the line above in the example. though do such requires `self.anything` which I need avoid a memory cycle 3) Hah! 4) e.g. you mean the title is dynamically coming from server which could be a success or failure and then the closure's doing would changed based on the title? – mfaani Nov 30 '16 at 17:19
  • 1
    4) You might have a series of dynamic buttons so the titles are not hardcoded. And then the handler might need to pass the chosen title off to some other method call. It's just a possible example. – rmaddy Nov 30 '16 at 17:21
  • about needing to refer to anything with self. I do understand the necessity of doing that for a property, but I don't understand the necessity of doing that for a function. Can you explain? – mfaani Nov 30 '16 at 17:56
  • That's a completely separate discussion. I suggest you post a new question specific to that (after ensuring it's not a duplicate). – rmaddy Nov 30 '16 at 17:58
1

rmaddy's answer is enough, but being the OP :) I find the root cause of my question :

My lack in understanding the parameter handler, handler is of type: (UIAlertAction) -> Void)? ie a closure/function

The handler is only expecting a function name, once you provide that function, it will fill in the inputs of that function itself.

you only pass anything as the handler ie function name.

UIAlertAction's internal implementation has some line like handler(action) which would use anything which is of type(UIAlertAction) -> Void)?, inject action (which is passed onto it) eventually doing anything(action).

That being said, your anything(input: UIAlertAction) should be do something meaningful. (already discussed in the comments with rmaddy)


The other solution is to not provide a function, but instead use the captured action in the trailing closure and do whatever you like doing with it.

let retryAction = UIAlertAction(title: returnTitle(), style: UIAlertActionStyle.Default) { action in
switch action.title {
        case "OK":
        okFunc()
        case "cancel":
        cancelFunc()
        default:
        defaultFunc()
        }
      }

Such switching is only valuable if you are getting the code dynamically otherwise there is no added value switching for a tile you know it's value.

mfaani
  • 33,269
  • 19
  • 164
  • 293
0
  1. UIAlerAction init method definition takes 3 argument and the last argument is (UIAlertAction -> Void)? which means you can have a ‘no-name’ function (aka closure) or a nil (specified by optional symbol ?). If you choose to not specify the last argument you can create UIAlertController like so

    alert.addAction(UIAlertAction(title: "test", style: .default, handler: nil))
    

    but if you want to specify the last argument (not nil) then you have to provide a closure that take UIAlertAction as an argument and return nothing (Void). Referring your link, he just use ‘alert’ constant that he already created for simplicity.

    To answer your question Does this mean…, the answer is Yes, because of the definition of the 3rd argument: (UIAlertAction) -> Void)?

  2. You get the error because you pass the argument (action). Try let

    anotherAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: anything)
    

    and you should not get error.

  3. UIAlertAction has only (I believe) one init method so you need to provide all three argument to create it. see my 1) answer for style: .default. This is a new way of calling Swift Enums. By the way all enum has to be lowercase so .default not .Default

    alert.addAction(UIAlertAction(title: String?, style: UIAlertActionStyle, handler: ((UIAlertAction) -> Void))
    
  4. As you can see from the answer you have the link to. You can use the constant alert (UIAlertAction) to do whatever you want with it, like checking the style and do some stuff.

    func anything(alert: UIAlertAction!) {
      print("somehandler")
      switch alert.title {
      case "OK"?:
        print("title is OK")
      default:
        print("title is not OK")
      }
    }
    
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Ohmy
  • 2,201
  • 21
  • 24
  • 1) OK 2) it's same as what I wrote, and still wrong. 3) correct 4) I don't understand what you mean there – mfaani Nov 30 '16 at 20:16
  • See new edited under 2) (I forgot to remove ( (action) after handler: anything)) and 4) is what I meant by do some stuff with alert in anything function. – Ohmy Nov 30 '16 at 22:51
  • what do you mean by **constant** 'action'? – mfaani Nov 30 '16 at 22:53
  • see the link you added the person create a constant let alert = UIAlertViewController(.... – Ohmy Nov 30 '16 at 22:56
  • no link has any mention of the "constant" I would appreciate if you would share. If you mean constant as in `let`, the usage of constant in your answer is *very* irrelevant. Does it make a difference if `let` is used or `var`? also you are talking about action in the context of it being captured. So again I'm confused why are you referring to it right before the closure. Removing it from your answer won't make your answer less useful. – mfaani Nov 30 '16 at 23:04
  • @Honey: 'let' definitions are constants i.e., you can not change them after assignment. Your first reference (under point 1) links to an article where 'let action' is defined i.e., 'action' is a constant. – Yohst Dec 01 '16 at 23:56