willowtreeapps / swift-style-guide

The official Swift style guide for WillowTree, Inc.
MIT License
16 stars 3 forks source link

Add section on UIKit specific work arounds/advice #10

Closed mattyohe closed 8 years ago

mattyohe commented 8 years ago
mattyohe commented 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
    }
}
mattyohe commented 8 years ago

An alternative method for injection: https://medium.com/@WoloxEngineering/viewmodel-injection-in-viewcontrollers-with-storyboards-50230143c3ac#.s383fhjb5

ianterrell commented 8 years ago

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? :)

benfrye commented 8 years ago

alternative method for injection

ianterrell commented 8 years ago

Updated for typo. :) Still not sure it's an alternative method for injection.

ianterrell commented 8 years ago

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.

ianterrell commented 8 years ago

At this point it's a weird place for conversation, but I added a generator PoC:

mattyohe commented 8 years ago

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.