Slide 1

Slide 1 text

Swinject ͱ DIKit Ͱ Dependency Injectionͯ͠Έͨ by. 2018/4/3 ROPPONGI.swift ୈ2ճ 1

Slide 2

Slide 2 text

takasek iOS Developer @takasek OSS ActionClosurable౳ Xcode Extension PasteTheType 2

Slide 3

Slide 3 text

Dependency Injection ґଘੑ஫ೖ 3

Slide 4

Slide 4 text

ڧͦ͏ͳ໊લ͚ͩͲ…ͭ·Γ͸ ඞཁͳ΋ͷΛ ֎͔Β౉͢͜ͱ1 1 iOSDCͰʮίʔυੜ੒ʹΑΔ੩తͳDependency Injectionʯʹ͍ͭͯ࿩ͨ͠ & ޱ಄ݪߘΛެ։ https://blog.ishkawa.org/2017/09/18/1505701774/ 4

Slide 5

Slide 5 text

αϯϓϧΞϓϦ • લճΞΫηε࣌ࠁͱ ݱࡏ࣌ࠁΛදࣔ • લճΞΫηε࣌ࠁ͸ UserDefaultsͰอଘ 5

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

⚠ UserDefaults (۩৅)΁ͷґଘ 7

Slide 8

Slide 8 text

UserDefaults΁ͷґଘΛ ந৅΁ͷґଘ ΁ͱॻ͖ସ͑Α͏ 8

Slide 9

Slide 9 text

final class UseCase { ... // ! ۩৅Λ let dateRepository = DateRepository() func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: Date()) ... } ... } 9

Slide 10

Slide 10 text

final class UseCase { ... // ! ந৅ʹ let dateRepository: DateRepositoryProtocol = DateRepositoryImpl() func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: Date()) ... } ... } 10

Slide 11

Slide 11 text

protocol DateRepositoryProtocol { func fetchLastDate() -> Date? mutating func saveCurrentDate(_ now: Date) } final class DateRepositoryImpl: DateRepositoryProtocol { func fetchLastDate() -> Date? { // UserDefaults ͔Βऔಘ ... } func saveCurrentDate(_ now: Date) { // UserDefaults ʹอଘ ... } } struct MockDateRepositoryImpl: DateRepositoryProtocol { var lastDate: Date? func fetchLastDate() -> Date? { return lastDate } mutating func saveCurrentDate(_ now: Date) { lastDate = now } } 11

Slide 12

Slide 12 text

DateRepositoryProtocol ґଘੑ ͸֎෦͔Β஫ೖ͠Α͏ 12

Slide 13

Slide 13 text

final class UseCase { ... let dateRepository: DateRepositoryProtocol = DateRepositoryImpl() // ܾΊଧͪͰ࢖͏ͷͰ͸ͳ͘… func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: Date()) ... } ... } 13

Slide 14

Slide 14 text

final class UseCase { ... let dateRepository: DateRepositoryProtocol // ! ඞࡴʂɹίϯετϥΫλɾΠϯδΣΫγϣϯʂ init(dateRepository: DateRepositoryProtocol) { self.dateRepository = dateRepository } func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: Date()) ... } ... } 14

Slide 15

Slide 15 text

final class UseCase { ... let dateRepository: DateRepositoryProtocol // ʘ͜ΕͰ׬੒ͩͱࢥͬͨʁʗ init(dateRepository: DateRepositoryProtocol) { self.dateRepository = dateRepository } func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: Date()) ... } ... } 15

Slide 16

Slide 16 text

final class UseCase { ... let dateRepository: DateRepositoryProtocol // ʘґଘੑ͸Զ͚ͩ͡Όͳ͍ͥʗ init(dateRepository: DateRepositoryProtocol) { self.dateRepository = dateRepository } func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: Date()) ... } ... } 16

Slide 17

Slide 17 text

final class UseCase { ... let dateRepository: DateRepositoryProtocol // ʘґଘੑ͸Զ͚ͩ͡Όͳ͍ͥʗ init(dateRepository: DateRepositoryProtocol) { self.dateRepository = dateRepository } func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: Date()) // ͋ͬ ... } ... } 17

Slide 18

Slide 18 text

protocol Clock { var now: Date { get } } struct SystemClock: Clock { var now: Date { return Date() } } struct MockClock: Clock { let now: Date } 18

Slide 19

Slide 19 text

final class UseCase { ... let dateRepository: DateRepositoryProtocol let clock: Clock // ! init(dependency: Dependency) { self.dateRepository = dependency.dateRepository self.clock = dependency.clock // ! } func load() { let lastDate = dateRepository.fetchLastDate() item = Item(lastDate: lastDate, now: clock.now()) // ... } ... } 19

Slide 20

Slide 20 text

ґଘੑ஫ೖ (Dependency Injection) ඞཁͳ΋ͷΛ ֎͔Β౉͢͜ͱ ͦΜ͚ͩ 20

Slide 21

Slide 21 text

෭࣍తޮՌ ͓ΊͰͱ͏ʂ ʮݱࡏ࣌ࠁΛදࣔ͢Δը໘ʯ͸ ʮ೚ҙͷ࣌ࠁΛදࣔ͢Δը໘ʯʹ ਐԽͨ͠ʂ 21

Slide 22

Slide 22 text

͜͏͍͏͜ͱ΍Γ͍ͨ struct SomeClock: Clock { var now: Date { ... } } // clock ΛҾ਺ʹ౉ͤ͹ // ΠΠײ͡ʹViewController͕ಘΒΕΔ let vc = ViewController( clock: SomeClock() ) self.pushViewController(vc, animated: true) 22

Slide 23

Slide 23 text

Ͱ΋…ʁ 23

Slide 24

Slide 24 text

Ͱ΋…ʁ final class UseCase { ... let dateRepository: DateRepositoryProtocol let clock: Clock init(dependency: Dependency) { self.dateRepository = dependency.dateRepository self.clock = dependency.clock } ... } 24

Slide 25

Slide 25 text

Ͱ΋…ʁ final class UseCase { ... let dateRepository: DateRepositoryProtocol let clock: Clock init(dependency: Dependency) { self.dateRepository = dependency.dateRepository self.clock = dependency.clock } ... } final class Presenter { let useCase: UseCase init(dependency: Dependency) { useCase = dependency.useCase ... } } 25

Slide 26

Slide 26 text

Ͱ΋…ʁ final class UseCase { ... let dateRepository: DateRepositoryProtocol let clock: Clock init(dependency: Dependency) { self.dateRepository = dependency.dateRepository self.clock = dependency.clock } ... } final class Presenter { let useCase: UseCase init(dependency: Dependency) { useCase = dependency.useCase ... } } final class ViewController: UIViewController { ... var presenter: Presenter! ... } 26

Slide 27

Slide 27 text

ݱ࣮͸ ͜͏Ͱ͢ let repository = DateRepositoryImpl() let clock = SystemClock() let useCase = UseCase(dependency: .init( dateRepository: repository, clock: clock )) let presenter = Presenter(dependency: .init( useCase: useCase )) let vc = UIStoryBoard(...).instantiate() vc.presenter = presenter 27

Slide 28

Slide 28 text

ClockΛ࡞ͬͨΒ DateRepositoryͱҰॹʹ UseCaseʹDIͯ͠ ͦΕΛPresenterʹDIͯ͠ ͦΕΛViewControllerʹDI 28

Slide 29

Slide 29 text

Dependency Graph 29

Slide 30

Slide 30 text

ຊ౰ʹ౉͍ͨ͠ͷ͸ Clock͚ͩ 30

Slide 31

Slide 31 text

άϥϑͷղܾΛ Ҿ͖ड͚ΔͨΊʹ DI framework͕͋Δ 31

Slide 32

Slide 32 text

DI framework ಈతDI framework (ϥϯλΠϜͰͷղܾ) • DIίϯςφʢSwinject, Cleanseʣ ੩తDI framework (ίϯύΠϥʹΑΔղܾ) • DIKitʢίʔυੜ੒Λ׆༻ʣ • Cake PatternʢProtocol ExtensionͰmix-inʣ • https://qiita.com/pab_tech/items/1c0bdbc8a61949891f1f • Reader Monad • https://speakerdeck.com/to4iki/di-with-reader-monad 32

Slide 33

Slide 33 text

Swinject 33

Slide 34

Slide 34 text

Swinject • Yoichi Tagaya͞Μ࡞ • ଟػೳͳDIίϯςφ •Initialization Callback •Object Scopes •Container Hierarchy •etc • Extension΋ॆ࣮ •SwinjectPropertyLoader •SwinjectStoryboard •Swinject-CodeGen •SwinjectAutoregistration 34

Slide 35

Slide 35 text

Without Swinject let repository = DateRepositoryImpl() let clock = SystemClock() let useCase = UseCase(dependency: .init( dateRepository: repository, clock: clock )) let presenter = Presenter(dependency: .init( useCase: useCase )) let vc = UIStoryBoard(...).instantiate() vc.presenter = presenter 35

Slide 36

Slide 36 text

With Swinject only let clock = SystemClock() as Clock let vc = container.resolve(ViewController.self, argument: clock) With Swinject & SwinjectStoryboard let vc = SwinjectStoryboard .create(name: "...", bundle: nil) .instantiateViewController(withIdentifier: "...") 36

Slide 37

Slide 37 text

Swinjectͷ࢖͍ํ // جຊߏ଄: ܕ͝ͱʹΫϩʔδϟΛregister͢Δ ! ͦΕΛresolveͰݺͿ defaultContainer.register(DateRepositoryProtocol.self) { _ in DateRepositoryImpl() } // ΫϩʔδϟͷୈೋҾ਺Ҏ߱Ͱɺresolve࣌ʹ࢖͏Ҿ਺Λࢦఆ͢Δ͜ͱ΋Ͱ͖Δ defaultContainer.register(UseCase.self) { (r: Resolver, clock: Clock) in UseCase(dependency: .init( dateRepository: r.resolve(DateRepositoryProtocol.self)!, // άϥϑͷղܾ clock: clock )) } defaultContainer.register(Presenter.self) { (r: Resolver, clock: Clock) in Presenter(dependency: .init( useCase: r.resolve(UseCase.self, argument: clock)! )) } 37

Slide 38

Slide 38 text

Swinject + SwinjectStoryboard ͷ࢖͍ํ // ܕʹՃ͑ͯ name Λࢦఆͯ͠register΋Ͱ͖Δ defaultContainer.register(Clock.self, name: "ViewController") { _ in return SystemClock() } // SwinjectStoryboard ͷ͍͢͝ͱ͜Ζ // StoryboardͰVC͕࡞ΒΕͨλΠϛϯάͰϑοΫͯ͠ // ϓϩύςΟɾΠϯδΣΫγϣϯͰ͖Δ defaultContainer.storyboardInitCompleted(ViewController.self) { r, vc in let clock = r.resolve(Clock.self, name: "ViewController")! vc.presenter = r.resolve(Presenter.self, argument: clock)! } 38

Slide 39

Slide 39 text

Swinject ͷ Pros / Cons Pros • ͱʹ͔͘ଟػೳ • ༷ʑͳΠϯδΣΫγϣϯͷύλʔϯ΍ɺάϥϑղܾ࣌ͷࡉ΍͔ͳཁ๬ʹରԠ Cons • ྑ͘΋ѱ͘΋ɺΫϩʔδϟΛઃఆ͢ΔಈతελΠϧ • IUO(!)͕සൃ͠ɺܕ҆શͰ͸ͳ͍ • ౉͢΂͖argumentsͳͲΛ஌Ζ͏ͱͯ͠΋ิ׬͕ར༻Ͱ͖ͳ͍ • ࢖͍͜ͳͨ͢Ίͷֶशίετ 39

Slide 40

Slide 40 text

ٕज़ॻయ4 ͸ 4/22(೔) @ळ༿ݪUDX! 40

Slide 41

Slide 41 text

DIKit 41

Slide 42

Slide 42 text

DIKit • ishkawa ͞Μ࡞ • iOSDCͰʮίʔυੜ੒ʹΑΔ੩తͳ Dependency Injectionʯʹ͍ͭͯ ࿩ͨ͠ & ޱ಄ݪߘΛެ։ https://blog.ishkawa.org/ 2017/09/18/1505701774/ 42

Slide 43

Slide 43 text

Without DIKit let repository = DateRepositoryImpl() let clock = SystemClock() let useCase = UseCase(dependency: .init( dateRepository: repository, clock: clock )) let presenter = Presenter(dependency: .init( useCase: useCase )) let vc = ViewController.makeInstance(dependency: init( presenter: presenter )) 43

Slide 44

Slide 44 text

With DIKit let clock = SystemClock() let vc = appResolver.resolveViewController(clock: clock) 44

Slide 45

Slide 45 text

ґଘάϥϑΛղܾͯ͘͠ΕΔ Resolver ͱ͍ ͏ଘࡏΛߟ͑Δ ! ͷதͰɺDependency͸3छྨʹ෼ྨՄ 1. Ҿ਺ͱͯ͠౉͍ͨ͠΍ͭ •Clock 2. Resolver ͕άϥϑ຤୺Ͱੜ੒͢Δ΍ͭ •DateRepositoryProtocol 3. 2͕͋Ε͹҉໧తʹղܾͰ͖Δ΍ͭ •UseCase, Presenter, ViewController 2͸ϓϩάϥϚͷखͰॻ͘ͱͯ͠ɺ 3͸πʔϧʹΑΔࣗಈੜ੒Ͱ·͔ͳ͑Δʂɹ ͱ͍͏ൃ૝ 45

Slide 46

Slide 46 text

1. Resolver Λ༻ҙ͠ɺ Resolver ʹղܾ͍ͤͨ͞Dependency Λఆٛɾ࣮૷ protocol AppResolver: Resolver { func provideDateRepository() -> DateRepositoryProtocol } final class AppResolverImpl: AppResolver { func provideDateRepository() -> DateRepositoryProtocol { return DateRepositoryImpl() } } 46

Slide 47

Slide 47 text

2. ҉໧తʹղܾͰ͖ΔDependency͸… final class ViewController: UIViewController, PresenterDelegate { struct Dependency { let presenter: Presenter } static func makeInstance(dependency: Dependency) -> ViewController { let vc = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as! ViewController vc.presenter = dependency.presenter return vc } var presenter: Presenter! ... } final class Presenter: UseCaseDelegate { struct Dependency { let useCase: UseCase } let useCase: UseCase init(dependency: Dependency) { useCase = dependency.useCase ... } } 47

Slide 48

Slide 48 text

2. ҉໧తʹղܾͰ͖ΔDependency͸ϓϩτίϧͰറΔ final class ViewController: UIViewController, PresenterDelegate, FactoryMethodInjectable { struct Dependency { let presenter: Presenter } static func makeInstance(dependency: Dependency) -> ViewController { let vc = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as! ViewController vc.presenter = dependency.presenter return vc } var presenter: Presenter! ... } final class Presenter: UseCaseDelegate, Injectable { struct Dependency { let useCase: UseCase } let useCase: UseCase init(dependency: Dependency) { useCase = dependency.useCase ... } } 48

Slide 49

Slide 49 text

͜ΕͰάϥϑղܾͷͨΊͷࡐྉ͸ἧͬͨ • Resolver ʹղܾ͍ͤͨ͞Dependency͸ Resolver ʹ࣮૷ࡁ • Dependency graph͸ DIKitͰఆٛࡁΈͷϓϩτίϧ ( Injectable ͳͲ)Ͱറ͍ͬͯΔ ͱ͍͏Θ͚Ͱ εΫϦϓτ dikitgen Λ૸ΒͤΔͱ… ʢRun scriptʹॻ͍ͱ͘ͷ΋͍͍͔΋ʣ dikitgen $SRCROOT/$PROJECT --exclude Carthage > $SRCROOT/$PROJECT/AppResolver.generated.swift 49

Slide 50

Slide 50 text

AppResolver.generated.swift ͕ࣗಈੜ੒͞ΕΔ extension AppResolver { func resolveDateRepositoryProtocol() -> DateRepositoryProtocol { return provideDateRepository() } func resolveUseCase(clock: Clock) -> UseCase { let dateRepositoryProtocol = resolveDateRepositoryProtocol() return UseCase(dependency: .init(dateRepository: dateRepositoryProtocol, clock: clock)) } func resolvePresenter(clock: Clock) -> Presenter { let useCase = resolveUseCase(clock: clock) return Presenter(dependency: .init(useCase: useCase)) } func resolveViewController(clock: Clock) -> ViewController { let presenter = resolvePresenter(clock: clock) return ViewController.makeInstance(dependency: .init(presenter: presenter)) } } 50

Slide 51

Slide 51 text

DIKit ͷ Pros / Cons Pros • ܕ҆શ • ॻ͘ίʔυྔ͕ѹ౗తʹগͳ͍ • ࣗಈੜ੒ϕʔεͳͷͰมߋʹ΋ڧ͍ Cons • είʔϓ͕ͳ͍ • δΣωϨʔλͷ଎౓໰୊ • DI͕ඞཁͳܕ͕ɺInjectable ϓϩτίϧʹറΒΕΔ • ຊ౰ʹCons? ݩʑͦ͏͋Δ΂͖ͱ͍͏ߟ͑΋ʁ 51

Slide 52

Slide 52 text

Cake Pattern 52

Slide 53

Slide 53 text

Dependency Injection ͷܗࣜ2 • ίϯετϥΫλɾΠϯδΣΫγϣϯ • ηολʔɾΠϯδΣΫγϣϯ • ΠϯλϑΣʔεɾΠϯδΣΫγϣϯ Cake Pattern ͸ͲΕͰ͠ΐ͏ 2 Inversion of Control ίϯςφͱ Dependency Injection ύλʔϯ http://kakutani.com/trans/fowler/injection.html#FormsOfDependencyInjection 53

Slide 54

Slide 54 text

Dependency Injection ͷܗࣜ • ίϯετϥΫλɾΠϯδΣΫγϣϯ • ηολʔɾΠϯδΣΫγϣϯ • ΠϯλϑΣʔεɾΠϯδΣΫγϣϯ • Mix-in ΠϯδΣΫγϣϯ ! Cake Pattern͸͜Ε 54

Slide 55

Slide 55 text

Cake Pattern ͢΂ͯΛprotocol extensionʹد͍͖ͤͯɺ ґଘੑ͸protocolͰఆٛ͞ΕͨϓϩύςΟ͔Βऔಘ …ͱ͍͏ߟ͑ํ͕جຊʹ͋Δ3 3 ຋༁: "Cake Pattern: The Bakery from the Black Lagoon" - Okapies' Archive http://okapies.hateblo.jp/entry/2013/07/15/232456 55

Slide 56

Slide 56 text

ͦͷͨΊࠓճͷαϯϓϧΞϓϦ΁ͷద༻͸೉͍͠…͚ΕͲ Cake Pattern Λ࢖͓͏ͱͯ͠ग़དྷ্͕ͬͨ Cake Patternʹ ࣅͯඇͳΔԿ͔ ΋࣮૷ͯ͠͸Έ·ͨ͠ɻ 56

Slide 57

Slide 57 text

ࠓճͷ֤DI frameworkͷ ࣮૷αϯϓϧ͸ͪ͜Β ʢςετίʔυ΋͋Γ·͢ʣ https://github.com/takasek/ DependencyInjectionSample 57