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

Dependency Injection Pattern for iOS Apps

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Yoichi Tagaya Yoichi Tagaya
September 21, 2018

Dependency Injection Pattern for iOS Apps

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

Avatar for Yoichi Tagaya

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