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

Dependency Injection Pattern for iOS Apps

Yoichi Tagaya
September 21, 2018

Dependency Injection Pattern for iOS Apps

FrenchKit 2018 in Paris, September 20-21, 2018
https://frenchkit.fr/

Yoichi Tagaya

September 21, 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,6k+ 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 .7$ "SDIJUFDUVSF :FBST

    TJODFMBVODI  .77.  7*1&3  &YBNQMFPGBQSPEVDU 3*#T &OHJOFFST     6TFST  L . . 
  4. !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
  5. !ZPJDIJUHZ Depending on concretions / abstractions Depending on concretions (tight

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

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

    *NQMFNFOUBUJPO Protocol (Interface) 3FGFSFODF $PODSFUF5ZQF# 5IFTZTUFNJTDPOpHVSFE UPVTF$PODSFUF5ZQF#
  8. !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
  9. !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() }
  10. !ZPJDIJUHZ In a production app class MyViewModel { init(dep1: D1,

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

    name: String) 5ZQFBMJBTPGUVQMF % % % %FQFOEFODZ 1BSBNFUFST
  12. !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
  13. !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 ) }
  14. !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 )
  15. !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 )
  16. !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 } }
  17. !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 ❕
  18. !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 ❕
  19. !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 ❕
  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, database) = dependency self.id = id } }
  21. !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 ❕
  22. !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 ❕
  23. !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")!
  24. !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")!
  25. !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.
  26. !ZPJDIJUHZ More info, merci beaucoup! • 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