Type safe Storyboard Identifiers
Get rid off String type identifiers
I don’t like to have hardcoded Strings
anywhere in my codebase. If you use Storyboards in your project, you probably wrote a code similar to the following:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
I tried to eliminate a need of writing MyStoryboardName
and MyViewController
. If you make a typo, it might lead to a crash very easilly. At the time I was looking into an awesome implementation of Kickstarter application. I noticed that they have a really neat way to instantiate a View Controller. Let’s take a look…
enum Storyboard: String {
case Main
case Preferences
}
extension Storyboard {
func instantiate<C: StoryboardIdentifiable>(_ viewController: C.Type, inBundle bundle: Bundle = .main) -> C {
guard let vc = NSStoryboard(name: self.rawValue, bundle: bundle)
.instantiateController(withIdentifier: C.storyboardIdentifier) as? C
else {
fatalError("Couldn't instantiate \(C.storyboardIdentifier) from \(self.rawValue)")
}
return vc
}
}
To make it work though we have to create a StoryboardIdentifiable
protocol.
protocol StoryboardIdentifiable {
var storyboardIdentifier: String { get }
static var storyboardIdentifier: String { get }
}
It’s super easy to create a default implementation of this protocol. We just need to return a String
based on self
’s type.
extension StoryboardIdentifiable {
var storyboardIdentifier: String {
return String(reflecting: self).components(separatedBy: ".").dropFirst().joined(separator: ".")
}
static var storyboardIdentifier: String {
return String(reflecting: self).components(separatedBy: ".").dropFirst().joined(separator: ".")
}
var description: String {
return storyboardIdentifier
}
}
With this implementation, if you call storyboardIdentifier
on MyViewController
, you will receive "MyViewController"
.
Now, we can make desired class conforming to it. In my case it was NSViewController
and NSWindowController
.
extension NSViewController: StoryboardIdentifiable { }
extension NSWindowController: StoryboardIdentifiable { }
That’s it. We can give it a spin now. In order to do so, we need to set an identifier of specific View Controller in Storyboard the same as its name. Then we can instantiate it as follows:
let myViewController = Storyboard.Main.instantiate(MyViewController.self)
It’s neat, isn’t it? Here you can find the whole implementation I use in Napi.