Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you create anonymous inner classes in Swift?

I'm tired of declaring entire classes as having the ability to handle UIAlertView clicks by making them extend UIAlertViewDelegate. It starts to feel messy and wrong when I have multiple possible UIAlertViews, and have to distinguish which was clicked in the handler.

What I really want is to create a single object that implements the UIAlertViewDelegate protocol, and give this one-off object to my UIAlertView when showing it.

I want something like this:

let confirmDelegate = UIAlertViewDelegate() {
    func alertView(alertView: UIAlertView!, clickedButtonAtIndex buttonIndex: Int) {
        // Handle the click for my alertView
    }
}

And then use it when showing the alert:

let alertView = UIAlertView(title: "Confirm", message: "Are you sure?", delegate: confirmDelegate, cancelButtonTitle: "No", otherButtonTitles: "Yes")
alertView.show()

Is this possible without declaring a new class?

I understand I could do something like this:

class ConfirmDelegate: UIAlertViewDelegate {
    func alertView(alertView: UIAlertView!, clickedButtonAtIndex buttonIndex: Int) {
        // ...
    }
}

And then instantiate a ConfirmDelegate(), but I'm just wondering if this is possible as one-liner class declaration and instantiation.

like image 667
Sean Adkinson Avatar asked Aug 11 '14 15:08

Sean Adkinson


2 Answers

As @ChrisWagner states in his comment, you shouldn't need to do any of this in iOS8, at least for UIAlertView since there is a new UIAlertViewController that uses closures without any delegates. But from an academic point of view, this pattern is still interesting.

I wouldn't use anonymous class at all. I would just create a class that can be assigned as the delegate and accept closures to execute when something happens.

You could even upgrade this to accept a closure for each kind of action: onDismiss, onCancel, etc. Or you could even make this class spawn the alert view, setting itself as the delegate.

import UIKit

class AlertViewHandler: NSObject, UIAlertViewDelegate {
    typealias ButtonCallback = (buttonIndex: Int)->()
    var onClick: ButtonCallback?

    init(onClick: ButtonCallback?) {
        super.init()
        self.onClick = onClick
    }

    func alertView(alertView: UIAlertView!, clickedButtonAtIndex buttonIndex: Int) {
        onClick?(buttonIndex: buttonIndex)
    }
}


class ViewController: UIViewController {

    // apparently, UIAlertView does NOT retain it's delegate.
    // So we have to keep it around with this instance var
    // It'll be nice when all of UIKit is properly Swift-ified :(
    var alertHandler: AlertViewHandler?

    func doSoemthing() {
        alertHandler = AlertViewHandler({ (clickedIndex: Int) in
            println("clicked button \(clickedIndex)")
        })

        let alertView = UIAlertView(
            title: "Test",
            message: "OK",
            delegate: alertHandler!,
            cancelButtonTitle: "Cancel"
        )
    }
}

Passing around closures should alleviate the need for anonymous classes. At least for the most common cases.

like image 101
Alex Wayne Avatar answered Nov 09 '22 16:11

Alex Wayne


Unfortunately as far as I understand it, no you cannot effectively create anonymous inner classes. The syntax you suggest would be really nice IMO though.

Here is my attempt at getting something close to what you want, it's no where near as clean though.

import UIKit

class AlertViewDelegate: NSObject, UIAlertViewDelegate {
    func alertView(alertView: UIAlertView!, clickedButtonAtIndex buttonIndex: Int) {

    }

    func alertView(alertView: UIAlertView!, didDismissWithButtonIndex buttonIndex: Int) {

    }

    func alertView(alertView: UIAlertView!, willDismissWithButtonIndex buttonIndex: Int) {

    }

    func alertViewCancel(alertView: UIAlertView!) {

    }

    func alertViewShouldEnableFirstOtherButton(alertView: UIAlertView!) -> Bool {
        return true
    }
}


class ViewController: UIViewController {
    var confirmDelegate: AlertViewDelegate?

    func doSoemthing() {
        confirmDelegate = {

            class ConfirmDelegate: AlertViewDelegate {
                override func alertView(alertView: UIAlertView!, clickedButtonAtIndex buttonIndex: Int) {
                    println("clicked button \(buttonIndex)")
                }
            }

            return ConfirmDelegate()
        }()

        let alertView = UIAlertView(title: "Test", message: "OK", delegate: confirmDelegate, cancelButtonTitle: "Cancel")
    }

}
like image 32
Chris Wagner Avatar answered Nov 09 '22 16:11

Chris Wagner