Reputation: 790
Very much appreciated the article about getting rid of using strings when instantiating UIViewController
or UIStoryboard
.
Though there is one point where I wanted to change the behavior. Instead of giving a Storyboard enum into the class method for getting a storyboard, I wanted a type here that conforms to a protocol.
extension UIStoryboard {
class func storyboard(storyboard: StoryboardRepresentable, bundle: NSBundle? = nil) -> UIStoryboard {
return UIStoryboard(name: storyboard.storyboardName, bundle: bundle)
}
}
protocol StringRawRepresentable: RawRepresentable {
typealias RawValue = String
var rawValue: String { get }
}
protocol StoryboardRepresentable {
var storyboardName: String { get }
}
extension StoryboardRepresentable where Self: StringRawRepresentable {
var storyboardName: String {
return self.rawValue
}
}
enum SomeOtherEnum: String, StoryboardRepresentable {
case BlaMain
case BlaSub
case BlaSomeThing
var storyboardName: String { return self.rawValue }
}
With that (given you have several moduls interesting in using this implementation) the models themselves could have new enum types conforming to StoryboardRepresentable instead of having a centralized enum with knowledge about all storyboards in use and thus creating a dependency.
And here is my problem. Though I have implemented the storyboardName in the extension, I got a compiler error complaining about non-protocol conformance when I remove the storyboardName on SomeOtherEnum!?
Upvotes: 1
Views: 1509
Reputation: 408
You can manage Storyboards and ViewController instantiation with help of several protocols/extensions.
UIStoryboardInstantiatable is a wrapper for generic instantiation methods:
public protocol UIStoryboardInstantiatable {
func instantiate<T: UIViewController>(controller: T.Type) -> T?
func instantiateInitial<T: UIViewController>(controller: T.Type) -> T?
func instantiateInitial() -> UIViewController?
}
public extension UIStoryboardInstantiatable {
func instantiateInitial() -> UIViewController? {
return instantiateInitial(controller: UIViewController.self)
}
}
public extension UIStoryboardInstantiatable where Self: UIStoryboardRepresentable {
func instantiate<T: UIViewController>(controller: T.Type) -> T? {
return storyboard.instantiate(controller: T.self)
}
func instantiateInitial<T: UIViewController>(controller: T.Type) -> T? {
return storyboard.instantiateInitial(controller: T.self)
}
}
public extension UIStoryboardInstantiatable where Self: UIStoryboard {
func instantiate<T: UIViewController>(controller: T.Type) -> T? {
return instantiateViewController(withIdentifier: T.className) as? T
}
func instantiateInitial<T: UIViewController>(controller: T.Type) -> T? {
return instantiateInitialViewController() as? T
}
}
In the future all our storyboards will be UIStoryboardRepresentables.
public protocol UIStoryboardRepresentable: UIStoryboardInstantiatable {
var storyboard: UIStoryboard { get }
var bundle: Bundle { get }
}
public extension UIStoryboardRepresentable where Self: RawRepresentable, Self.RawValue == String {
var storyboard: UIStoryboard {
return UIStoryboard.init(name: rawValue, bundle: bundle)
}
}
Now you can create list of storyboards presented in your application:
public enum AppStoryboard: String, UIStoryboardRepresentable {
case components
case main
case news
public var bundle: Bundle {
return Bundle.main
}
public var rawValue: String {
return "\(self)".capitalizingFirstLetter()
}
}
Small String extension for capitalizing first letter of rawValue. Because by convention Storyboard names are pascal case and the enum cases are camel case.
extension String {
public func capitalizingFirstLetter() -> String {
return prefix(1).uppercased() + dropFirst()
}
}
Usage:
let controller = AppStoryboard.main.instantiate(controller: TestViewController.self)
From project to project only AppStoryboard is changing.
Upvotes: 0
Reputation: 790
extension UIStoryboard {
class func storyboard(storyboard: StoryboardRepresentable, bundle: NSBundle? = nil) -> UIStoryboard {
return UIStoryboard(name: storyboard.storyboardName, bundle: bundle)
}
}
protocol StoryboardRepresentable {
var storyboardName: String { get }
}
extension StoryboardRepresentable where Self: RawRepresentable, Self.RawValue == String {
var storyboardName: String {
return self.rawValue
}
}
enum SomeOtherEnum: String, StoryboardRepresentable {
case BlaMain
case BlaSub
case BlaSomeThing
}
StoryboardRepresentable
can now be applied to any enum of type String
but not to type Int
.
Upvotes: 4
Reputation: 285069
StringRawRepresentable
is not directly related to StoryboardRepresentable
.
Since the protocol extension affects only StoryboardRepresentable
objects which also conform to StringRawRepresentable
you have to declare SomeOtherEnum
enum SomeOtherEnum: String, StoryboardRepresentable, StringRawRepresentable {
Upvotes: 0