神刀安全网

Update: View Controller Data Injection with Storyboards and Segues in Swift

Last week, I wrote about how I do View Controller Data Injection with Storyboards and Segues in Swift

TL;DR – It’s not an ideal solution, but I use Implicitly Unwrapped Optionals (IUOs) that I make sure to use in viewDidLoad (or earlier in the ViewController lifecycle) so there is an explicit :boom: if it’s not set.

Since then, many have chimed in with solution they use, but honestly, they all seem super complicated, ugly, and unreadable. Many mentioned this is why they don’t use storyboards, etc. So I was ok with my not-so-great, but simple, solution.

However, yesterday I read a comment by @cduhn on my I :sparkling_heart: Storyboards & Nibs blog post that triggered an idea for a slightly better solution.

Here is the comment:

“I look forward to using the implicitly unwrapped var trick when I migrate to Swift. For now, here’s the idiom I like to use for dependency injection with storyboards in Objective C:

1. Each view controller has a public method starting with setDependencies like this:

- (void)setDependenciesViewModel:(XYZViewModel *)viewModeldelegate:(id <xyzviewcontrollerdelegate>)delegate {   self.viewModel = viewModel;   self.delegate = delegate; } 

2. Each view controller has a private method called -assertDependencies, which I call at the top of viewDidLoad.

- (void)assertDependencies {     // injected dependencies     NSParameterAssert(         self.viewModel &&         self.delegate);     // also a great way to remember those IBOutlet connections!     NSParameterAssert(         self.titleLabel &&         self.collectionView &&         self.collectionView.datasource &&         self.collectionView.delegate); }   - (void)viewDidLoad {       [super viewDidLoad];       [self assertDependencies]; } 

3. Now simply call setDependencies in prepareForSegue:

- (void)prepareForSegue:(UIStoryboardSegue *)seguesender:(id)sender {     if ([segue.identifierisEqualToString:XYZSegue]) {         XYZViewController *viewController = segue.destinationViewController;         NSParameterAssert([viewControllerisKindOfClass:[XYZViewController class]]);         [viewControllersetDependenciesViewModel:[self viewModelForSelectedThing]delegate:self];     } } 

One of the things I like about this idiom is that all the dependencies of the class are bundled up in a single method signature, just like a good initializer. If you forget to inject a dependency, you find out about it at compile time.

So here is a more Swifty version of this solution that I’m playing with:

First, create an Injectable Protocol:

protocol Injectable {     associatedtype T     func inject(thing: T)     func assertDependencies() } 

Now, my Implicitly Unwrapped Optional dependency can be private:

class RedPillViewController: UIViewController, Injectable {       @IBOutletweak privatevar mainLabel: UILabel!          // this will be renamed to associatedtype in future versions of Swift     // the type matches the IOU's type     typealias T = String          // this is my original dependency (IOU)     // I can now make this private!     privatevar mainText: String!          override func viewDidLoad() {         super.viewDidLoad()                  // this will crash if the IOU is not set         assertDependencies()                  // using the IOU if needed here,         // but using it later is fine as well         mainLabel.text = mainText     }          // Injectable Implementation     func inject(thing: T) {         mainText = thing     }          func assertDependencies() {         assert(mainText != nil)     } } 

Now, in prepareForSegue, we can use the inject method:

//  ViewController.swift       override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {                  switch segueIdentifierForSegue(segue) {                      case .TheRedPillExperience:             let redPillVC = segue.destinationViewController as? RedPillViewController             redPillVC?.inject(":smiling_imp:")         case .TheBluePillExperience:             let bluePillVC = segue.destinationViewController as? BluePillViewController             bluePillVC?.inject(":angel:")         }     } 

The thing I really like about this is that it standardizes the inject method for all ViewControllers that need dependency injection. It’s easy to just start typing “inject” in prepareForSegue to see if something needs to be injected vs trying to remember the exact thing that needs to be set!

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Update: View Controller Data Injection with Storyboards and Segues in Swift

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮