Issue to inherit UIAlertAction in Swift
Asked Answered
G

3

5

Here is my code:

class CustomAlertAction: UIAlertAction {
    init(title : String) {
        super.init(title: title, style: UIAlertActionStyle.Default) { (action) -> Void in
        }
    }
}

But I got the following compiling error:

Must call a designated initializer of the superclass 'UIAlertAction'

I know the designated initializer of UIAlertAction is init(). But the init(title, style, handler) of UIAlert will not call its designated initializer init()?

Any idea? Thanks

P.S.: Based on the Apple's document:

A designated initializer must call a designated initializer from its immediate superclass.”

Does this mean it's not allowed to inherit UIAlertAction in Swift? It's no problem to do so in Objective-C.

The reason why I want to create a subclass of UIAlertAction is because I want to add a ReactiveCocoa command as an action.

Glengarry answered 14/7, 2015 at 23:52 Comment(6)
Why do you think you need to subclass UIAlertAction?Baram
It's unfortunately, but you can do a workaround, see https://mcmap.net/q/272728/-swift-must-call-a-designated-initializer-of-the-superclass-skspritenode-errorQuantum
"The reason why I want to create a subclass of UIAlertAction is because I want to add a ReactiveCocoa command as an action" And you can't do that with an extension?Baram
Who downvote my post? Isn't my question a valid question? I don't know WTF these guys are doing. Strongly suggest ask them to give the reason.Glengarry
I ended up to create my own view controller.Glengarry
Hmm. Interesting problem. The title and style properties are readOnly as well so you can't even call the designated initialiser and just set the properties separately. Seems like an oversight from apple.Arrival
S
9

The solution is realistically almost certainly to use class extensions instead.

extension UIAlertAction {
    convenience init(title: String) {
        self.init(title: title, style: .Default, handler: nil)
    }
}

Usage:

let okayAction = UIAlertAction(title: "Okay")

You can still subclass the UIAlertAction to add properties to it. The subclass can still use this convience initializer you extended off of UIAlertAction class.


Finally, for what it's worth, someone has already created a Reactive Cocoa UIAlertAction subclass. They just did it in Objective-C. Seeing how there's no harm in adding Objective-C to your project, you could just take this approach... or just install that pod...

Sectionalize answered 15/7, 2015 at 0:27 Comment(5)
The reason I want to create subclass instead of extension is because I'm going to use some stored properties. Anyway this could be a valid solution if there is no stored properties. Thanks.Glengarry
@bagusflyer You can store properties using Objective-C runtime. Or you can subclass, but simply add your initializers in an extension (to UIAlertAction).Sectionalize
This is a smart suggestion. I'll do that. Using extension for init and subclass without init. Thanks. +1Glengarry
Still, it's not very elegant. For example, I want to add a viewmodel for a UIAlertController in its init. If I add it in the extension, the interface will be exposed publicly. Am I right?Glengarry
@bagusflyer Take a look at my final paragraph. Is there a reason you can't use that pod or write your own Objective-C subclass? You could even write an Objective-C subclass that only implements the init method(s) and then write an extension for that subclass to add any other methods to it you want.Sectionalize
S
1

Like @nhgrif said, using an extension is the way to go. It's a complementary way to write expressiveness code.

Example:

/// App alert actions
extension UIAlertAction {
    static var cancel: UIAlertAction {
        return UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
    }
    class func sharePhoto(handler: ((UIAlertAction) -> Void)?) -> UIAlertAction {
        return UIAlertAction(title: "Share", style: .Default, handler: handler)
    }
}

Use it like

alertController.addAction(.cancel)

alertController.addAction(.sharePhoto({ action in
    print(action)
}))
Shrievalty answered 28/10, 2016 at 19:58 Comment(0)
B
0

I came across this post while researching the same problem. It's been a few years since this was posted, so probably not helpful to the OP at this point, but will be for anyone searching this issue.

After seeing this post, I did some further research. It turns out it is in fact possible to subclass UIAlertAction with a custom init at least with the version of Swift used at the time of this writing. I'll answer as if directed to the OP anyways.

You need to define your init as a convenience init, and the one you are currently calling as super.init is also a convenience init. Only designated initializers can be called on super classes (hence the error). I also wanted to reference the UIAlertController that the action is attached to in my use case, so I have an instance variable in play too. I'm also using my own UIAlertController subclass here.

class MyAlertAction: UIAlertAction {
    var alertController: MyAlertController? = nil

    convenience init(
        alertController: MyAlertController,
        title: String?,
        style: UIAlertAction.Style,
        handler: ((UIAlertAction) -> Void)?
    ) {
        // init must be called before instance variables and functions are referenced
        self.init(title: title, style: style, handler: handler)
        self.alertController = alertController
    }
}

The reason this works is because either no designated initializers have been defined in the subclass, or all designated initializers from the superclass have been implemented by the subclass. In these two cases, all of the convenience initializers from the superclass are inherited down (except any with matching signatures in the subclass). This is the only way to call a convenience initializer from a superclass because as mentioned before, super.init is limited to designated initializers.

Because there is no implementation of any designated initializers in my example, the subclass also inherits all designated initializers from the superclass. So by not defining any designated initializers, all of the initializers from the superclass are available as if they were defined in the subclass.

There is a whole write-up on initialization in the Swift book available at https://docs.swift.org/swift-book/LanguageGuide/Initialization.html. Of particular interest in this case are these four subsections:

  1. Initializer Delegation for Class Types: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID219
  2. Two-Phase Initialization: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID220
  3. Initializer Inheritance and Overriding: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID221
  4. Automatic Initializer Inheritance: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID222
Belting answered 21/3, 2021 at 20:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.