简体   繁体   中英

Suggestions for dependency injection to view controllers in swift

I'm making an iOS app with Swift that has different states for most of the view controllers composing the app. The few of the "states" the view controllers depend on are whether the user is logged in or not, or if an address is either registered, searched, or missing, etc.. Currently, the data are given to the view controllers in prepare(for:sender) methods.

The following is the structure of my app, and there are about a dozen more view controllers with the similar structure.

App.swift

struct App {
  enum LoginState {
    case unregistered
    case registered(User) // User defined elsewhere
  }

  enum OtherState {
    case stateOne
    case stateTwo(AssociatedType)
    case stateThree(OtherAssociatedType)
  }

  // Default states
  var loginState: LoginState = .unregistered
  var otherState: OtherState = .stateOne
}

HomeViewController.swift

class HomeViewController: UIViewController {
  var app: App!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)

    switch segue.identifier {
    case "Other View Controller Segue":
      let otherVC = segue.destination as! OtherViewController
      otherVC.app = app

    default:
      break
    }
}

AppDelegate.swift

class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let storybard = UIStoryboard(name: "Main", bundle: nil)
    let mainNavController = storyboard.instantiateViewController(withIdentifier: "Main Navigation Controller") as! UINavigationController
    let homeViewController = mainNavController.topViewController as! HomeViewController

    window?.rootViewController = mainNavController
    // 'injecting' app to the homeViewController
    homeViewController.app = App()

    return true
}

I can't find the original post what I found when I first encountered "how can I pass data between view controllers" about a year ago, but this is probably one of them: Passing Data between View Controllers (which does not mention the term dependency injection anywhere). Then I heard this term dependency injection about a month ago, and the fancy term seemed to fit my situation of handling a class ( HomeViewController and OtherViewController ) that needs to deal with data that can have states ( struct App ).

To verify this approach of tossing app instance in a segue, I've done some several nights of research. But now I'm overloaded with too much information about: several dependency injection methodologies , unit testing in swift via DI , a few DI frameworks here , here , here , and here , unit testing frameworks here , here , and (now I can't add more links due to the lack of reputation..), and a whole bunch of medium / blog / SO posts.

The question is: 1. Am I doing the so-called dependency injection here, and is this the "right way"? 2. Can I proceed unit/UI tests with this approach? 3. If what I am doing is a legit dependcy injection, why would one need a dependency injection framework?

Dependency injection frameworks look unnecessary, myterical , (and scary), at least for me simply trying to write a maintainable app that follows SOLID principles. I'm tired of abstract concepts or seemingly unrelated examples with my situation, so I'd be glad if someone help me out with these concepts and design decisions, including the need of frameworks.

I would suggest avoid focusing on the technical details of the different type of DI, and I would focus on the basic concept. DI means that your view controller doesn't get the dependencies it needs by itself, but the dependencies are provided from outside . So your otherVC.app = app is a very tiny basic example of DI.

In a nutshell you can now test your view controller passing different instances of app and this is the main goal of doing DI.

Can you do better? Well, probably. For example making your App object a bit smaller, dividing the "state" in smaller pieces. Another improvement could be using a protocol instead of a concrete type in your view controller. In this way, during the test, you can pass a mock object as dependency and this helps you a lot for the tests.

I agree with IgnazioC's answer and want to add some things.

I think you could ask you this questions to get whether your implementaion is a good example of DI.

  • Can I get any of my consumer(viewController) and run it with new dependencies that I set it from outside. If answer is yes. It is a good practice for DI.
  • How easy I can do this and how easy I can write required code to use this. This answers decides how much your practice is good.

Your code solves your problem. But I think(maybe I am wrong about this) this creates another problems.

  • Deciding a viewControllers' dependencies from another viewController is a good idea? Really, Is it a viewController's job? or it should be?. I don't think it is a good practice in most cases.

  • What happens when you have scenario that a chain of viewControllers present themselves and most of your dependencies(like user info) must carried between first and last viewController's of this chain but not all. This creates some copy codes. Maybe some of this can managable with composite pattern or something like this but not for all.

I think (Maybe I am wrong again) carrying dependencies between consumers is not a good idea. At least mostly. Maybe you will write an enough little app to manage this problems. But most cases, dependencies should be resolved outside of viewController chains.

Note: I don't include dependecies that created from previous contoller like happens in master detail scenarios. They are different thing.

In swift, this is some kind of hard for now. Maybe swift needs some improvements do this more easyly(like a better reflector). But, I find an atricle about DI usage in swift 5.1. I haven't use this yet. But it seems a useful solution. Maybe this could help you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM