Return instancetype in Swift

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Return instancetype in Swift



I'm trying to make this extension:


extension UIViewController

class func initialize(storyboardName: String, storyboardId: String) -> Self

let storyboad = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! Self

return controller




But I get compile error:



error: cannot convert return expression of type 'UIViewController' to
return type 'Self'



Is it possible? Also I want to make it as init(storyboardName: String, storyboardId: String)


init(storyboardName: String, storyboardId: String)





Please don't post answers inside your question.
– Rizier123
Nov 7 '16 at 16:29




3 Answers
3



Similar as in Using 'self' in class extension functions in Swift, you can define a generic helper method which infers the type of self from the calling context:


extension UIViewController

class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self

return instantiateFromStoryboardHelper(storyboardName, storyboardId: storyboardId)


private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T

let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier(storyboardId) as! T
return controller




Then


let vc = MyViewController.instantiateFromStoryboard("name", storyboardId: "id")



compiles, and the type is inferred as MyViewController.


MyViewController



Update for Swift 3:


extension UIViewController

class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self

return instantiateFromStoryboardHelper(storyboardName: storyboardName, storyboardId: storyboardId)


private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T

let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) as! T
return controller




Another possible solution, using unsafeDowncast:


unsafeDowncast


extension UIViewController

class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self

let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: storyboardId)
return unsafeDowncast(controller, to: self)






Hi Martin, Could you please explain how the compiler be able to identify the generic type T and be able to cast it to Self?
– Adithya
Sep 10 '16 at 6:13





people who say "private class func" rather than "static func" have a lot of style. :)
– Fattie
Feb 5 '17 at 15:21





it's interesting that if you just have it return the base type - don't bother with a generic - it works perfectly at runtime (returning the actual subclass) but you'd have to cast results in code, at "editor time". example: stackoverflow.com/a/42053648/294884
– Fattie
Feb 5 '17 at 15:34





@JoeBlow: Yes, the goal here was just to avoid the cast. – "static" is "class + final" and therefore unrelated to "private".
– Martin R
Feb 5 '17 at 16:29





quite. thanks @MartinR. It took me days to figure the answer to this conceptually similar issue stackoverflow.com/q/42041150/294884
– Fattie
Feb 5 '17 at 19:48



Self is determined at compile-time, not runtime. In your code, Self is exactly equivalent to UIViewController, not "the subclass that happens to be calling this." This is going to return UIViewController and the caller will have to as it into the right subclass. I assume that's what you were trying to avoid (though it is the "normal Cocoa" way to do it, so just returning UIViewController is probably the best solution).


Self


Self


UIViewController


UIViewController


as


UIViewController



Note: You should not name the function initialize in any case. That's an existing class function of NSObject and would cause confusion at best, bugs at worst.


initialize


NSObject



But if you want to avoid the caller's as, subclassing is not usually the tool to add functionality in Swift. Instead, you usually want generics and protocols. In this case, generics are all you need.


as


func instantiateViewController<VC: UIViewController>(storyboardName: String, storyboardId: String) -> VC
let storyboad = UIStoryboard(name name: storyboardName, bundle: nil)
let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! VC

return controller



This isn't a class method. It's just a function. There's no need for a class here.


let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)





This is a great answer. I had a very similar problem a while back. I wanted a protocol that classes that could be instantiated from a storyboard could implement, so that the caller didn't have to know the exact type of view controller being instantiated. I gave up on that approach. Maybe I'll try it again, but with a generic in the protocol.
– NRitH
Oct 18 '15 at 16:30



Another way is to use a protocol, which also allows you to return Self.


Self


protocol StoryboardGeneratable



extension UIViewController: StoryboardGeneratable



extension StoryboardGeneratable where Self: UIViewController

static func initialize(storyboardName: String, storyboardId: String) -> Self

let storyboad = UIStoryboard(name: storyboardName, bundle: nil)
let controller = storyboad.instantiateViewController(withIdentifier: storyboardId) as! Self
return controller







By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

make 2 or more post in bootsrap

Store custom data using WC_Cart add_to_cart() method in Woocommerce 3

Firebase Auth - with Email and Password - Check user already registered