Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIViewController extension to instantiate from storyboard

I'm trying to write a little extension in Swift to handle instantiation of a UIViewController from a storyboard.

My idea is the following: Since UIStoryboard's method instantiateViewControllerWithIdentifier needs an identifier to instantiate a given storyboard's view controller, why don't assign every view controller in my storyboard an identifier equal to its exact class name (i.e a UserDetailViewController would have an identifier of "UserDetailViewController"), and, create a class method on UIViewController that would:

  • accept a UIStoryboard instance as a unique parameter
  • get the current class name as a string
  • call instantiateViewControllerWithIdentifier on the storyboard instance with the class name as a parameter
  • get the newly created UIViewController instance, and return it

So, instead of (which repeats the class name as a string, not very nice)

let vc = self.storyboard?.instantiateViewControllerWithIdentifier("UserDetailViewController") as UserDetailViewController

it would be:

let vc = UserDetailViewController.instantiateFromStoryboard(self.storyboard!)

I used to do it in Objective-C with the following category:

+ (instancetype)instantiateFromStoryboard:(UIStoryboard *)storyboard
{
    return [storyboard instantiateViewControllerWithIdentifier:NSStringFromClass([self class])];
}

But I'm completely stuck with the Swift version. I hope is that there is some kind of way to do it. I tried the following:

extension UIViewController {
    class func instantiateFromStoryboard(storyboard: UIStoryboard) -> Self {
        return storyboard.instantiateViewControllerWithIdentifier(NSStringFromClass(Self))
    }
}

Returning Self instead of AnyObject allows the type inference to work. Otherwise, I would have to cast every single return of this method, which is annoying, but maybe you have a better solution?

This gives me the error: Use of unresolved identifier 'Self' The NSStringFromClass part seems to be the problem.

What do you think?

  • Is there any way to return Self from class functions?

  • How would you get this working without the need to cast the return value every time? (i.e keeping -> Self as return value)

like image 396
Nicolas B. Avatar asked Sep 26 '14 09:09

Nicolas B.


1 Answers

How about writing an extension to UIStoryboard instead of UIViewController?

extension UIStoryboard {
    func instantiateVC<T: UIViewController>() -> T? {
        // get a class name and demangle for classes in Swift
        if let name = NSStringFromClass(T.self)?.componentsSeparatedByString(".").last {
            return instantiateViewControllerWithIdentifier(name) as? T
        }
        return nil
    }

}

Even adopting this approach, cost of an use side is low as well.

let vc: UserDetailViewController? = aStoryboard.instantiateVC()
like image 109
findall Avatar answered Oct 05 '22 14:10

findall