Jonas Schmid
Jonas Schmid

Reputation: 5491

Dynamically check an object type in Swift 2

I am building the state restoration of an iOS app. I currently have

func application(application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [AnyObject], coder: NSCoder) -> UIViewController? {

    guard let controllerIdentifier = identifierComponents.last as? String else {
        return nil
    }

    guard let ctrl = self.window?.rootViewController else {
        return nil
    }

    if controllerIdentifier == "SetupPagesViewController" && ctrl is SetupPagesViewController {
        return ctrl
    } else if controllerIdentifier == "MainViewController" && ctrl is MainViewController {
        return ctrl
    }

    return nil
}

Where I find that the last line are a bit ugly. I will potentially have more ifs that will always return the controller. I am trying to find a construct where I would not have all those ifs.

I tried with things like:

    let checks = [
        "SetupPagesViewController": SetupPagesViewController.self,
        "MainViewController": MainViewController.self,
    ]

    if let clazz = checks[controllerIdentifier] where ctrl is clazz {
        return ctrl
    }

But the compiler does not let me. I cannot find a way to store my class type in order to reuse it in the if.

Is that possible? How? Thanks

Upvotes: 2

Views: 242

Answers (3)

rshev
rshev

Reputation: 4176

Think this Swifty approach will work for you:

func checkController(controllerIdentifier: String, ctrl: Any) -> UIViewController? {
    let objectList: [String:Any.Type] = [
        "SetupPagesViewController": SetupPagesViewController.self,
        "MainViewController": MainViewController.self
    ]

    let typeOfCtrl: Any.Type = Mirror(reflecting: ctrl).subjectType
    if objectList[controllerIdentifier] == typeOfCtrl {
        return ctrl as? UIViewController
    }

    return nil
}

Upvotes: 0

Kametrixom
Kametrixom

Reputation: 14973

How about this:

typealias NSViewController = Any

class A : NSViewController {}
class B : NSViewController {}
class C : NSViewController {}

let classes : [AnyClass] = [
    A.self,
    B.self,
    C.self
]

func isValid(ctrl: NSViewController, controllerIdentifier: String) -> NSViewController? {
    if classes.contains({ controllerIdentifier == String($0) && ctrl.dynamicType == $0 }) {
        return ctrl
    } else {
        return nil
    }
}

(the typealias is just to simplify it, doesn't matter really). This works with any type, you also don't need to provide names for the classes because String(someClass) gives you the name. I used contains here because I think you just want to check whether it's a valid view controller, which gets returned if it is.

Upvotes: 0

You can use the introspection facilities of the Objective-C runtime (exposed by Foundation) for this, namely, the isKindOfClass method of NSObject:

let checks: [String:AnyClass] = [
    "SetupPagesViewController": SetupPagesViewController.self,
    "MainViewController": MainViewController.self,
]

if let clazz = checks[controllerIdentifier] where ctrl.isKindOfClass(clazz) {
    return ctrl
}

Please note that for this to work, the classes have to inherit from NSObject.

Upvotes: 1

Related Questions