Pure DI 101

Df8bc8c531e2c5c89c1a007db1cf79a3?s=47 Yu Sugawara
January 26, 2019

Pure DI 101

DevSap勉強会 19/01/26
https://devsap.connpass.com/event/113851/

(前回)iOSアプリのアーキテクチャについて考えてたどり着いた結論
https://speakerdeck.com/yusuga/final-conclusion-of-the-ios-architecture

Df8bc8c531e2c5c89c1a007db1cf79a3?s=128

Yu Sugawara

January 26, 2019
Tweet

Transcript

  1. Pure DI ೖ໳ DevSapษڧձ 19/01/26

  2. ੁݪ ༞ @yusuga_

  3. DI࢖ͬͯ·͔͢ʁ • Dependency Injection (DI) • ґଘΦϒδΣΫτͷ஫ೖ • ͦͷଞͷΩʔϫʔυͱͯ͠DIίϯςφ •

    iOS։ൃऀͳΒ99.9%࢖ͬͨ͜ͱ͋ΔσβΠϯύλʔϯ
  4. ґଘͱ͸ • ʮΫϥεAΛਖ਼͘͠ಈ͔͢ͷʹΫϥεB͕ඞཁʯͱ͍͏ঢ়ଶͳΒ ʮΫϥεA͸ΫϥεBʹґଘ͍ͯ͠Δʯͱݴ͑Δ

  5. ґଘΦϒδΣΫτͷ஫ೖํ๏ • ίϯετϥΫλɾΠϯδΣΫγϣϯ • SwiftͳΒΠχγϟϥΠβʔɾΠϯδΣΫγϣϯ • ηολʔɾΠϯδΣΫγϣϯ

  6. ྫ • ڞ௨ͷఆٛ protocol Service { func requestUsers() } class

    Twitter: Service { func requestUsers() { /* Twitter API */ } } class Instagram: Service { func requestUsers() { /* Instagram API */ } }
  7. DIͳ͠ class UsersViewController: UIViewController { let service = Twitter() //

    UsersViewController͸Twitterʹґଘ͍ͯ͠Δ func reload() { service.requestUsers() } } let vc = UsersViewController() vc.reload() // ΋͠΋ґଘΛInstagramʹม͍͑ͨ৔߹͸௚઀ίʔυΛฤू͢Δඞཁ͕͋Δ
  8. ίϯετϥΫλɾΠϯδΣΫγϣϯ class UsersViewController: UIViewController { let service: Service // ґଘΦϒδΣΫτʢϓϩτίϧͰந৅Խʣ

    init(service: Service) { // ॳظԽ࣌ʹґଘΦϒδΣΫτΛ஫ೖ self.service = service super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func reload() { service.requestUsers() } }
  9. ίϯετϥΫλɾΠϯδΣΫγϣϯ let vc = UsersViewController(service: Twitter()) let vc = UsersViewController(service:

    Instagram())
  10. ηολʔɾΠϯδΣΫγϣϯ class UsersViewController: UIViewController { var service: Service? func reload()

    { service?.requestUsers() } } let vc = UsersViewController() vc.service = Twitter() // ηολʔͰґଘΦϒδΣΫτΛ஫ೖ vc.reload()
  11. ηολʔɾΠϯδΣΫγϣϯͷར༻γʔϯ • ίϯετϥΫλɾΠϯδΣΫγϣϯͰ͖ͳ͍৔߹ • ྫ͑͹Storyboard࢖༻࣌ let vc = UIStoryboard(name: "Main",

    bundle: nil) .instantiateInitialViewController()! as! UsersViewController vc.service = Twitter() // ηολʔͰґଘΦϒδΣΫτΛ஫ೖ
  12. IBOutlet͸ηολʔɾΠϯδΣΫγϣϯͰղܾ͞ΕΔ class UsersViewController: UIViewController { // Storyboard(or Xib)͔ΒΠϯδΣΫγϣϯ͞ΕΔ @IBOutlet weak

    var button: UIButton! } • ViewControllerΫϥεͷॳظԽޙɺґଘΦϒδΣΫτʢ্هͩͱ buttonʣ͕ࣗಈͰηολʔɾΠϯδΣΫγϣϯͰղܾ͞ΕΔ • ͳͷͰXcodeͰσϑΥϧτͰએݴ͞ΕΔܕ΋UIButton!ʹͳͬ ͍ͯΔɻ
  13. IBOutlet͸ηολʔɾΠϯδΣΫγϣϯͰղܾ͞ΕΔ class UsersViewController: UIViewController { // Storyboard(or Xib)͔ΒΠϯδΣΫγϣϯ͞ΕΔ @IBOutlet weak

    var button: UIButton! } • ͨͩ͠ɺxmlϑΝΠϧͱΫϥε಺ͷએݴ͕Ұகͯ͠ͳ͔ͬͨΒϥ ϯλΠϜͰΫϥογϡ͢Δ • ޙड़͢ΔಈతDIίϯςφͷσϝϦοτ
  14. DIͱ͸ • DIͱ͍͏༻ޠΛ஌Βͳ͍ਓͰ΋࢖ͬͨ͜ͱ͋ΔσβΠϯύλʔ ϯ • iOS։ൃऀͳΒIBOutlet͸99.9%࢖ͬͨ͜ͱ͋Δʢ͸ͣ • Α͘࿩୊ʹ্͕ΔDIͷ࿩͸ɺϥΠϒϥϦΛ࢖ͬͯ؆୯ʹDI͠Α ͏ͱ͍͏࿩

  15. DIίϯςφͱ͸ • DIίϯςφ͸ґଘؔ܎ΛղܾͰ͖ΔΦϒδΣΫτ • Storyboard΍Xib͸ಈతDIίϯςφ • ViewController΍ViewΛਖ਼͘͠ಈ࡞͢ΔͨΊͷView৘ใ΍Ϩ ΠΞ΢τͳͲͷґଘΛهड़ͨ͠xmlϑΝΠϧ • StoryboardͷΑ͏ͳDIίϯςφ͸iOS։ൃͩͱಛघͰɺίʔυͰ

    DIίϯςφΛ࡞੒͢Δ
  16. DIͷछྨ छྨ ґଘؔ܎ͷղܾํ๏ DI (≒ Pure DI, Vanilla DI) खಈ,

    DIίϯςφΛ࢖༻͠ͳ͍DI ಈతDIίϯςφ DIίϯςφͰ ಈత ʹґଘؔ܎ Λղܾ ੩తDIίϯςφ DIίϯςφͰ ੩త ʹґଘؔ܎ Λղܾ
  17. ओͳϥΠϒϥϦ (Swift੡) ϥΠϒϥϦ Github Star DIͷछผ Swinject 2,922 ಈతDIίϯςφ Cleanse

    1,168 ಈతDIίϯςφ Dip 725 ಈతDIίϯςφ DIKit 242 ੩తDIίϯςφ Pure 186 Pure DI Github Star਺͸19/1/26࣌఺
  18. Pure DIͱ͸ • Pure DI by Mark Seemann • ಈతDIίϯςφΛ࢖༻͠ͳ͍Ͱɺίϯ

    ύΠϧ࣌ʹ͢΂ͯͷґଘؔ܎Λ੩తʹ ղܾͤ͞ΔDI • ڧ͍੩తܕ෇͚ݴޠͷSwiftʹϚον ͍ͯ͠Δ
  19. ಈతDIίϯςφͷσϝϦοτ • ಈతDIίϯςφͷ࣮૷ϛε͸ϥϯλΠϜͰ͔͠ݕ஌Ͱ͖ͳ͍ • ྫ͑͹StoryboardͱఆٛͷෆҰக͸ϥϯλΠϜͰΫϥογϡ ͯ͠ؾͮ͘ • ࣮૷ϛε͸ίϯύΠϧͷ࣌఺Ͱݕ஌ɾղܾ͍ͨ͠…

  20. Pure DI in Swift • Pure • ࡞ऀ͸ReactorKitͱಉ͡ • v1.0.0

    • Pure DIΛ΍Γ΍͘͢͢Δํ๏Λఏڙ͠ ͍ͯΔ͚ͩͳͷͰίʔυྔ͕গͳ͍ • ͨͩ͠αϯϓϧϓϩδΣΫτͳ͠… • ׆༻͢ΔͨΊʹ͸ҰൠతͳDIͷख๏ ͕Θ͔Βͳ͍ͱϐϯͱ͜ͳ͍͔΋͠ Εͳ͍
  21. ίʔυͰൺֱղઆ • DI: खಈ • ಈతDIίϯςφ: Swinject • Pure DI:

    Pure
  22. DI: खಈʢલड़ʣ class UsersViewController: UIViewController { let service: Service //

    ґଘΦϒδΣΫτ init(service: Service) { // ॳظԽ࣌ʹґଘΦϒδΣΫτΛ஫ೖ self.service = service super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func reload() { service.requestUsers() } }
  23. DI: खಈʢલड़ʣ // ίϯετϥΫλɾΠϯδΣΫγϣϯ let vc = UsersViewController(service: Twitter()) vc.reload()

  24. ಈతDIίϯςφ: Swinject let container = Container() // SwinjectͰఆٛ͞Ε͍ͯΔίϯςφΫϥε container.register(Service.self) {

    _ in Twitter() } // ServiceΛొ࿥ container.register(UsersViewController.self) { r in // UsersViewControllerΛొ࿥ UsersViewController( service: r.resolve(Service.self)! // Twitter͕ฦ͞ΕΔ ) } // ͜ͷ࣌఺Ͱcontainer͸͢΂ͯͷґଘΛղܾ͍ͯ͠Δ // containerʹొ࿥͍ͯͨ͠ґଘؔ܎Λղܾͨ͠UsersViewController͕ฦ͞ΕΔ let vc = container.resolve(UsersViewController.self)! vc.reload()
  25. PureͷఆٛʢҰ෦ʣ public protocol Module { associatedtype Dependency = Void //

    ࣄલʹܾΊΒΕΔґଘΦϒδΣΫτ associatedtype Payload = Void } /// A module that can be constructed with a factory. public protocol FactoryModule: Module { /// A factory for `Self`. associatedtype Factory = Pure.Factory<Self> /// Creates an instance of a module with a dependency and a payload. init(dependency: Dependency, payload: Payload) // ॳظԽ }
  26. Pure DI: Pure class UsersViewController: UIViewController, FactoryModule { struct Dependency

    { // ґଘΦϒδΣΫτ let service: Service } let service: Service required init(dependency: Dependency, payload: ()) { service = dependency.service super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func reload() { service.requestUsers() } }
  27. Pure DI: Pure let factory = UsersViewController.Factory( dependency: UsersViewController.Dependency( service:

    Twitter() // ґଘΦϒδΣΫτ ) ) // ͜ͷfactory͸ґଘؔ܎Λղܾͨ͠FactoryΦϒδΣΫτ // ґଘؔ܎Λղܾͨ͠UsersViewControllerΛੜ੒ͯ͠ฦ͢ let vc = factory.create() vc.reload()
  28. ґଘΦϒδΣΫτ + ಈతʹܾΊ͍ͨ஋

  29. • UsersViewControllerͷఆٛΛগ͠มߋ class UsersViewController: UIViewController { let service: Service //

    ґଘΦϒδΣΫτ let userName: String // ಈతʹܾΊ͍ͨ஋ init(service: Service, userName: String) { self.service = service self.userName = userName super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func reload() { service.requestUsers() } }
  30. DI: खಈ let vc = UsersViewController( service: Twitter(), // ґଘΦϒδΣΫτ

    userName: "yusuga" // ಈతʹܾΊ͍ͨ஋ ) vc.reload()
  31. ಈతDIίϯςφ: Swinject let container = Container() // SwinjectͰఆٛ͞Ε͍ͯΔίϯςφΫϥε container.register(Service.self) {

    _ in Twitter() } // ServiceΛొ࿥ container.register(UsersViewController.self) { r, userName in UsersViewController( service: r.resolve(Service.self)!, // Twitter͕ฦ͞ΕΔ userName: userName // resolve࣌ʹಈతʹܾΊ͍ͨ஋͕౉͞ΕΔ ) } // ͜ͷ࣌఺Ͱcontainer͸͢΂ͯͷґଘΛղܾ͍ͯ͠Δ let vc = container.resolve( UsersViewController.self, // ґଘΦϒδΣΫτ͸ొ࿥ࡁΈ argument: "yusuga" // ಈతʹܾΊ͍ͨ஋ )! vc.reload()
  32. ஫ҙ: Swinjectͷargument͸ܕͷνΣοΫ͕ͳ͍ let container = Container() // SwinjectͰఆٛ͞Ε͍ͯΔίϯςφΫϥε container.register(Service.self) {

    _ in Twitter() } // ServiceΛొ࿥ container.register(UsersViewController.self) { r, userName in UsersViewController( service: r.resolve(Service.self)!, // Twitter͕ฦ͞ΕΔ userName: userName // resolve࣌ʹಈతʹܾΊ͍ͨ஋͕౉͞ΕΔ ) } // ͜ͷ࣌఺Ͱcontainer͸͢΂ͯͷґଘΛղܾ͍ͯ͠Δ let vc = container.resolve( UsersViewController.self, argument: 1 // userName͸Stringܕ͕ͩίϯύΠϧ͸௨Δ ) // ܕ͕ҰகͤͣґଘΛղܾͰ͖ͳ͍৔߹͸nil͕ฦΔ vc.reload()
  33. PureͷఆٛʢҰ෦ʣ public protocol Module { associatedtype Dependency = Void //

    ࣄલʹܾΊΒΕΔґଘΦϒδΣΫτ associatedtype Payload = Void // ͦͷΫϥεΛੜ੒͢Δͱ͖ʹܾΊ͍ͨಈతͳ஋ } /// A module that can be constructed with a factory. public protocol FactoryModule: Module { /// A factory for `Self`. associatedtype Factory = Pure.Factory<Self> /// Creates an instance of a module with a dependency and a payload. init(dependency: Dependency, payload: Payload) }
  34. Pure DI: Pure class UsersViewController: UIViewController, FactoryModule { struct Dependency

    { // ґଘΦϒδΣΫτ let service: Service } struct Payload { // ಈతʹܾΊ͍ͨ஋ let userName: String } let service: Service let userName: String required init(dependency: Dependency, payload: Payload) { service = dependency.service userName = payload.userName super.init(nibName: nil, bundle: nil) } /* ҎԼলུ */ }
  35. Pure DI: Pure let factory = UsersViewController.Factory( dependency: UsersViewController.Dependency( service:

    Twitter() ) ) // ͜ͷfactory͸ґଘؔ܎Λղܾͨ͠FactoryΦϒδΣΫτ // ґଘؔ܎Λղܾͨ͠UsersViewControllerΛFactory͔Βੜ੒͢Δͱ͖ʹ // PayloadʢಈతʹܾΊ͍ͨ஋ʣΛղܾ͢Δ let vc = factory.create( payload: UsersViewController.Payload( userName: "yusuga" ) ) vc.reload()
  36. Pure͕ఏڙ͍ͯ͠Δ΋ͷ1 • ࣄલʹܾఆͰ͖ΔґଘΦϒδΣΫτ(Dependency)Λղܾͨ͠ FactoryΫϥε let factory = UsersViewController.Factory( dependency: UsersViewController.Dependency(

    service: Twitter() ) ) // ͜ͷfactory͸ґଘؔ܎Λղܾͨ͠FactoryΦϒδΣΫτ
  37. Pure͕ఏڙ͍ͯ͠Δ΋ͷ2 • Factory͔ΒͦͷΫϥεΛੜ੒͢Δͱ͖ʹɺಈతʹܾΊ͍ͨ஋ (Payload)ΛࢦఆͰ͖Δcreateؔ਺ let vc = factory.create( payload: UsersViewController.Payload(

    userName: "yusuga" ) )
  38. ·ͱΊ • DIͳ͠ɺDI, ಈతDIίϯςφͷ࠷ऴతʹੜ੒͞ΕΔ΋ͷ͸ಉ͡ ͰɺͦͷաఔͰґଘؔ܎ΛͲ͏͢Δ͔ͱ͍͏࿩ • ಈతDIίϯςφ͸࣮૷ϛε͸ϥϯλΠϜͰ͔͠ݕ஌Ͱ͖ͳ͍ͱ ͍͏σϝϦοτ͕͋Δ • ࣮ͨͩ૷͸ָ

    • Swinject͸ߴػೳͰ΄ͱΜͲͷύλʔϯΛղܾͰ͖Δ
  39. ·ͱΊ • ࠓճ঺հ͍ͯ͠·ͤΜ͕ɺDIKit͸ಈతDIίϯςφͷϥϯλΠϜ Ͱ͔͠ϛεΛݕ஌Ͱ͖ͳ͍෦෼Λิͬͨ ੩త DIίϯςφΛ࠾༻ • Build phasesͰιʔε಺༰͔ΒDIίϯςφΫϥεϑΝΠϧΛੜ ੒Ͱ͖ΔShell

    scriptΛఏڙ • ͨͩɺv0.4.0ʢ19/01/26࣌఺ʣͱ͍͏͜ͱ΋͔͋ͬͯɺ·ͩ एׯͷػೳෆ଍ײ͸͋Δ
  40. ·ͱΊ • Pure͸ಈతDIίϯςφΛ࢖༻ͤͣ੩తʹґଘؔ܎Λղܾͤ͞Δ ੔ཧํ๏Λఏڙ • Dependency͸ࣄલʹܾఆͰ͖ΔґଘΦϒδΣΫτɺPayload ͸ͦͷΫϥεੜ੒࣌ʹܾఆ͍ͨ͠ಈతͳ஋ͱ͍͏੔ཧํ๏ • ଞʹੜ੒࣌ʹPayload͕ܾΊΒΕͳ͍৔߹ʹ࢖༻͢Δ ConfiguratorΫϥε΋͋Δ

    • 2.5ਓ݄ͷϓϩδΣΫτͰಋೖͯ͠͏·͘ߦͬͨ
  41. ࣍ͷؔ৺ • ը໘ؒɺը໘ભҠͷґଘΛͲ͏΍ͬͯ ղܾ͢Δ͔ • RxSwiftCommunity/RxFlow • Reactive Flow Coordinator

    pattern • ͪ͜Β΋2.5ਓ݄ͷϓϩδΣΫτʹ࣮ઓ ౤ೖͯ͠खԠ͑͋ͬͨʢPure͚ͩͰ͸ ղܾͰ͖ͳ͍໰୊͕͋ͬͨͷͰҰॹʹ ಋೖʣ
  42. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ