Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dependency Injection

Dependency Injection

DevFest Baltics 2018 in Jurmala, 16 November, 2018
https://devfest2018.gdg.lv

Yoichi Tagaya

November 16, 2018
Tweet

More Decks by Yoichi Tagaya

Other Decks in Programming

Transcript

  1. !ZPJDIJUHZ About me as Swinject author • Swinject: dependency injection

    framework for Swift • Maintained by 5 members • Recently by Jakub Vano especially! • 2,7k+ GitHub stars now and 1k clones everyday • 4 extensions provided
  2. !ZPJDIJUHZ Peter Eeles says An architecture • Defines structure and

    behavior • Focuses on significant elements • May conform to an architectural style • Influences team structure (inverse Conway maneuver) • Balances stakeholder needs • Is influenced by its environment • Embodies decisions based on rationale
  3. !ZPJDIJUHZ Architecture is not static but dynamic "SDIJUFDUVSF :FBST TJODFMBVODI

       &YBNQMFPGBQSPEVDU &OHJOFFST     6TFST  L . . 
  4. !ZPJDIJUHZ Architecture is not static but dynamic .7$ "SDIJUFDUVSF :FBST

    TJODFMBVODI  .77.  7*1&3  &YBNQMFPGBQSPEVDU 3*#T &OHJOFFST     6TFST  L . . 
  5. !ZPJDIJUHZ SOLID principles • S - Single responsibility principle •

    O - Open/closed principle • L - Liskov substitution principle • I - Interface segregation principle • D - Dependency inversion principle • Should not depend on concretions but abstractions
  6. !ZPJDIJUHZ Depending on concretions / abstractions Depending on concretions (tight

    coupling) *NQMFNFOUBUJPO $PODSFUF5ZQF Depending on abstractions (loose coupling) 3FGFSFODF *NQMFNFOUBUJPO Protocol (Interface) 3FGFSFODF $PODSFUF5ZQF" $PODSFUF5ZQF#
  7. !ZPJDIJUHZ Dependency injection: a way to realize dependency inversion principle

    *NQMFNFOUBUJPO Protocol (Interface) 3FGFSFODF *OTUBODFPG $PODSFUF5ZQF# 1BTT JOKFDU UPJOJUJBMJ[FS
  8. !ZPJDIJUHZ Dependency injection: a way to realize dependency inversion principle

    *NQMFNFOUBUJPO Protocol (Interface) 3FGFSFODF $PODSFUF5ZQF# 5IFTZTUFNJTDPOpHVSFE UPVTF$PODSFUF5ZQF#
  9. !ZPJDIJUHZ Code example of dependency injection protocol APIClientProtocol { ...

    } class APIClient: APIClientProtocol { ... } let vm = MyViewModel(apiClient: APIClient()) class MyViewModel { let apiClient: APIClientProtocol init(apiClient: APIClientProtocol) { self.apiClient = apiClient } } APIClientProtocol "CTUSBDUUZQF *OKFDUDPODSFUFUZQF
  10. !ZPJDIJUHZ Code example of registration $POUBJOFS Register import Swinject let

    container = Container() container.register(MyViewModel.self) { resolver in let apiClient = resolver.resolve(APIClientProtocol.self)! return MyViewModel(apiClient: apiClient) } container.register(APIClientProtocol.self) { _ in APIClient() }
  11. !ZPJDIJUHZ In a production app class MyViewModel { init(dep1: D1,

    dep2: D2, dep3: D3, id: String, name: String) { ... } } .BOZEFQFOEFODJFT %FQFOEFODZJOKFDUJPODPEFJTPGUFOMJLFUIJT
  12. !ZPJDIJUHZ In a production app class MyViewModel { init(dep1: D1,

    dep2: D2, dep3: D3, id: String, name: String) { ... } } .BOZEFQFOEFODJFT 3VOUJNFQBSBNFUFST PUIFSUIBOEFQFOEFODJFT %FQFOEFODZJOKFDUJPODPEFJTPGUFOMJLFUIJT
  13. !ZPJDIJUHZ Using typealias of dependency tuple init(dependency: Dependency, id: String,

    name: String) 5ZQFBMJBTPGUVQMF % % % %FQFOEFODZ 1BSBNFUFST
  14. !ZPJDIJUHZ Example of class definition with typealias of tuple protocol

    APIClientProtocol { ... } class APIClient: APIClientProtocol { ... } protocol LoggerProtocol { ... } class Logger: LoggerProtocol { ... } 5ZQFBMJBTPG EFQFOEFODZUVQMF class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol ) let (apiClient, logger): Dependency let id: String init(dependency: Dependency, id: String) { (apiClient, logger) = dependency self.id = id } } &YBNQMFXJUIEFQFOEFODJFTBOEQBSBNFUFS
  15. !ZPJDIJUHZ DI Registration let container = Container() container.register(APIClientProtocol.self) { _

    in APIClient() } container.register(LoggerProtocol.self) { _ in Logger() } $POUBJOFS Register container.register(MyViewModel.self) { (resolver, id: String) in let dependency = ( resolver.resolve(APIClientProtocol.self)!, resolver.resolve(LoggerProtocol.self)! ) return MyViewModel( dependency: dependency, id: id ) }
  16. !ZPJDIJUHZ Add another dependency let (apiClient, logger): Dependency let id:

    String init(dependency: Dependency, id: String) { (apiClient, logger) = dependency self.id = id } } class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol )
  17. !ZPJDIJUHZ Add another dependency protocol DatabaseProtocol { ... } class

    Database: DatabaseProtocol { ... } let (apiClient, logger): Dependency let id: String init(dependency: Dependency, id: String) { (apiClient, logger) = dependency self.id = id } } class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol )
  18. !ZPJDIJUHZ Add another dependency protocol DatabaseProtocol { ... } class

    Database: DatabaseProtocol { ... } class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol, DatabaseProtocol ) let (apiClient, logger): Dependency let id: String init(dependency: Dependency, id: String) { (apiClient, logger) = dependency self.id = id } }
  19. !ZPJDIJUHZ Add another dependency protocol DatabaseProtocol { ... } class

    Database: DatabaseProtocol { ... } class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol, DatabaseProtocol ) let (apiClient, logger): Dependency let id: String init(dependency: Dependency, id: String) { (apiClient, logger) = dependency self.id = id } } 8SPOHUVQMFIFSF ❕
  20. !ZPJDIJUHZ Add another dependency protocol DatabaseProtocol { ... } class

    Database: DatabaseProtocol { ... } class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol, DatabaseProtocol ) let (apiClient, logger, database): Dependency let id: String init(dependency: Dependency, id: String) { (apiClient, logger) = dependency self.id = id } } 8SPOHUVQMFIFSF ❕
  21. !ZPJDIJUHZ Add another dependency protocol DatabaseProtocol { ... } class

    Database: DatabaseProtocol { ... } class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol, DatabaseProtocol ) let (apiClient, logger, database): Dependency let id: String init(dependency: Dependency, id: String) { (apiClient, logger, database) = dependency self.id = id } } 8SPOHUVQMFIFSF ❕
  22. !ZPJDIJUHZ Add another dependency protocol DatabaseProtocol { ... } class

    Database: DatabaseProtocol { ... } class MyViewModel { typealias Dependency = ( APIClientProtocol, LoggerProtocol, DatabaseProtocol ) let (apiClient, logger, database): Dependency let id: String init(dependency: Dependency, id: String) { (apiClient, logger, database) = dependency self.id = id } }
  23. !ZPJDIJUHZ Add another dependency container.register(MyViewModel.self) { (resolver, id: String) in

    let dependency = ( resolver.resolve(APIClientProtocol.self)!, resolver.resolve(LoggerProtocol.self)! ) return MyViewModel( dependency: dependency, id: id ) } let myViewModel = container.resolve( MyViewModel.self, argument: "abc")!
  24. !ZPJDIJUHZ Add another dependency container.register(MyViewModel.self) { (resolver, id: String) in

    let dependency = ( resolver.resolve(APIClientProtocol.self)!, resolver.resolve(LoggerProtocol.self)! ) return MyViewModel( dependency: dependency, id: id ) } let myViewModel = container.resolve( MyViewModel.self, argument: "abc")! $BOOPUDPOWFSU ❕
  25. !ZPJDIJUHZ Add another dependency container.register(MyViewModel.self) { (resolver, id: String) in

    let dependency = ( resolver.resolve(APIClientProtocol.self)!, resolver.resolve(LoggerProtocol.self)! ) return MyViewModel( dependency: dependency, id: id ) } let myViewModel = container.resolve( MyViewModel.self, argument: "abc")! container.register(DatabaseProtocol.self) { _ in Database() } $BOOPUDPOWFSU ❕
  26. !ZPJDIJUHZ Add another dependency container.register(DatabaseProtocol.self) { _ in Database() }

    $BOOPUDPOWFSU ❕ container.register(MyViewModel.self) { (resolver, id: String) in let dependency = ( resolver.resolve(APIClientProtocol.self)!, resolver.resolve(LoggerProtocol.self)!, resolver.resolve(DatabaseProtocol.self)! ) return MyViewModel( dependency: dependency, id: id ) } let myViewModel = container.resolve( MyViewModel.self, argument: "abc")!
  27. !ZPJDIJUHZ Add another dependency container.register(DatabaseProtocol.self) { _ in Database() }

    container.register(MyViewModel.self) { (resolver, id: String) in let dependency = ( resolver.resolve(APIClientProtocol.self)!, resolver.resolve(LoggerProtocol.self)!, resolver.resolve(DatabaseProtocol.self)! ) return MyViewModel( dependency: dependency, id: id ) } let myViewModel = container.resolve( MyViewModel.self, argument: "abc")!
  28. !ZPJDIJUHZ Summary • Architecture is not static but dynamic to

    evolve. • Dependency injection is sticky-like glue. • Use a typealias to group dependencies. • Just follow Swift compiler to add new dependencies.
  29. !ZPJDIJUHZ More info, thanks! • Swinject Tutorial for iOS: Getting

    Started by Gemma Barlow at raywenderlich.com • iOS App Development: Design Patterns for Mobile Architecture by Jon Bott at lynda.com • Dependency injection in Swift by Manuel Munoz at blog.quadiontech.com • Adventures in Dependency Swinjection by Ben Dietzkis at medium.com/@topLayoutGuide • Swinject - Dependency Injection in iOS by Iron Ben Zvi at speakerdeck.com/oronbz