Sti
Sti

Reputation: 8503

Change return type of function based on class

If I have a class BaseViewController:UIViewController, and I want a way to instantiate this viewController from a Xib, I could do it like this:

class func initFromXib()->BaseViewController?{
    let nibName = String(NSStringFromClass(self).split(separator: ".").last ?? "")
    let nib = UINib(nibName: nibName, bundle: nil)
    return nib.instantiate(withOwner: nil, options: nil).first as? BaseViewController
}

Whenever I need the ViewController initialized from xib, I could then just say let vc = BaseViewController.initFromXib().

But let's say I have a subclass of this viewController, named HomeViewController:BaseViewController. If I say let home = HomeViewController.initFromXib(), this would indeed successfully return an instance of HomeViewController (as long as there's a Xib named as such), but as a BaseViewController. There's nothing wrong with this, all I have to do is

let vc = HomeViewController.initFromXib() as? HomeViewController

and everything is coolioo. But is there a way to make the return type of the initFromXib a "generic" type of the class it stems from, so that initFromXib always returns the type of the correct class?

Upvotes: 0

Views: 115

Answers (3)

Er.Gopal Vasani
Er.Gopal Vasani

Reputation: 448

Please use the below solution, You don't need to even use the Generics and TypeCasting as well.

extension UIViewController {

    static func initWithNibName() -> Self {
        return self.init(nibName: "\(self)", bundle: nil)
    }
}

How to use in code?

let homeVC = HomeViewController.initWithNibName()
print(homeVC) // type is HomeViewController

Upvotes: 1

vadian
vadian

Reputation: 285290

A solution is a protocol extension with associated type, just adopt the protocol in the appropriate classes

protocol XibInitializable {
    associatedtype ControllerType : UIViewController = Self
    static func initFromXib() -> ControllerType?
}

extension XibInitializable where Self : UIViewController {

    static func initFromXib() -> ControllerType? {
        let nibName = String(describing: Self.self)
        let nib = UINib(nibName: nibName, bundle: nil)
        return nib.instantiate(withOwner: nil, options: nil).first as? ControllerType
    }
}

Now you can write

let vc = HomeViewController.initFromXib()

Upvotes: 2

Joakim Danielson
Joakim Danielson

Reputation: 52108

Use generics

class func initFromXib<T: BaseViewController>()-> T?{
    let nibName = String(NSStringFromClass(self).split(separator: ".").last ?? "")
    let nib = UINib(nibName: nibName, bundle: nil)
    return nib.instantiate(withOwner: nil, options: nil).first as? T
}

Upvotes: 1

Related Questions