Closed mattyohe closed 8 years ago
Possible code snippet for Factory for storyboard backed UIViewControllers that require injected properties:
class ProfileViewController: UIViewController {
var user: User!
class func build(user: User) -> ProfileViewController {
let storyboard = UIStoryboard(name: "ProfileViewController", bundle: nil)
let controller = storyboard.instantiateInitialViewController() as! ProfileViewController
controller.user = user
return controller
}
}
An alternative method for injection: https://medium.com/@WoloxEngineering/viewmodel-injection-in-viewcontrollers-with-storyboards-50230143c3ac#.s383fhjb5
I like the article and approach; I'm not sure it's quite an alternative method for injection. It's just changing storage & still requiring injection happen somewhere. Unless I'm missing something? :)
alternative method for injection
Updated for typo. :) Still not sure it's an alternative method for injection.
I think storyboards can still be useful. I've been playing around with prepare
for injection on segue, analogous to build
for the factory method. For cases where you can be created both ways, prepare
should have nearly the same signature and should be used by build
.
final class A: UIViewController {
var dependency: String!
static func build(dependency dependency: String) -> A {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("InitialA") as! A
controller.prepare(dependency: dependency)
return controller
}
func prepare(dependency dependency: String) {
self.dependency = dependency
}
I also started playing with extending the protocol based segue helpers from WWDC that Natasha the Robot wrote up here: https://www.natashatherobot.com/protocol-oriented-segue-identifiers-swift/
In mine, I add a bit of type safety to the controller bit in the extensions to support the prepare
method usage.
With a new view controller B
:
final class B: UIViewController {
var dependency: String!
func prepare(dependency dependency: String) {
self.dependency = dependency
}
}
We can now have a segue ToB
from A
, which A
can call manually:
@IBAction func pushManually() {
performSegue(.ToB, sender: self)
}
And we can prepare for like so:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segueFromStoryboardSegue(segue) {
case .ToB(let b):
b.prepare(dependency: "injected from a")
}
}
In addition to the extension work: https://github.com/ianterrell/segue-builder/blob/master/SegueBuilder/SegueHandler.swift
It relies on the enums set up for A
:
extension A: SegueHandler {
enum SegueIdentifier: String {
case ToB
}
enum ToSegueDestination: SegueDestination {
case ToB(B)
init?(identifier: SegueIdentifier, destination: UIViewController) {
switch identifier {
case .ToB:
guard let b = destination as? B else { return nil }
self = .ToB(b)
}
}
}
}
On the plus side, the enums can be generated from storyboards. I'm working on a PoC build to do that.
At this point it's a weird place for conversation, but I added a generator PoC:
Still not sure it's an alternative method for injection.
I agree, and it would definitely need some sort of build(
method to actually inject. The approach from Salix seems better than this case because this feels like a time you want to crash early if we're improperly using properties we expect to be injected, and in the above blog's case, we just end up failing silently, or guarding a bunch.
Your other work in auto generation seems to be a a great approach for when you do want to use storyboards for more than one view controller and you're navigating via segues.
as!
when dequeuing cells to catch logic errors