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

SwiftUIにおける依存性逆転原則の戦略 / A strategy to import Dependency-Inversion-Principle in SwiftUI apps

Elvis Shi
February 24, 2021

SwiftUIにおける依存性逆転原則の戦略 / A strategy to import Dependency-Inversion-Principle in SwiftUI apps

Elvis Shi

February 24, 2021
Tweet

More Decks by Elvis Shi

Other Decks in Programming

Transcript

  1. } var employedBy = "YUMEMI Inc." var job = "iOS

    Tech Lead" var favoriteLanguage = "Swift" var twitter = "@lovee" var qiita = "lovee" var github = "el-hoshino" var additionalInfo = """ ݘ೿ͳͷʹೣΛอޢͯ͠͠·ͬͨ """ final class Me: Developable, Talkable {
  2. struct LandmarkList: View { @EnvironmentObject var modelData: ModelData @State private

    var showFavoritesOnly = false var body: some View { // ... } } IUUQTEFWFMPQFSBQQMFDPNUVUPSJBMTTXJGUVJ
  3. final class ModelData: ObservableObject { @Published var landmarks: [Landmark] =

    [ // ... ] } IUUQTEFWFMPQFSBQQMFDPNUVUPSJBMTTXJGUVJ
  4. ͳͥ۩৅ʹґଘ͢Δͷ͸ѱ͍จ໌͔ final class SomeComponent { var p: Int = 0

    func f() {} // ... } final class SomeObject { let component: SomeComponent init(component: SomeComponent) { self.component = component } func doSomething() { component.f() } }
  5. ͳͥ۩৅ʹґଘ͢Δͷ͸ѱ͍จ໌͔ final class SomeComponent { var p: Int = 0

    func f() {} // ... } final class SomeObject { let component: SomeComponent init(component: SomeComponent) { self.component = component } func doSomething() { component.f() } } func test_doSomething() { final class MockComponent: SomeComponent { override init() { super.init() self.p = 100 } override func f() {} } let object = SomeObject(component: MockComponent()) object.doSomething() } Inheritance from a final class 'SomeComponent' Instance method overrides a 'final' instance method
  6. ͳͥ۩৅ʹґଘ͢Δͷ͸ѱ͍จ໌͔ class SomeComponent { var p: Int = 0 func

    f() {} // ... } final class SomeObject { let component: SomeComponent init(component: SomeComponent) { self.component = component } func doSomething() { component.f() } } func test_doSomething() { final class MockComponent: SomeComponent { override init() { super.init() self.p = 100 } override func f() {} } let object = SomeObject(component: MockComponent()) object.doSomething() } pOBMDMBTTͰએݴͰ͖ͳ͍ͨΊ ύϑΥʔϚϯε͕ඍົʹྑ͘ͳ͍  ܧঝ͕๷͛ͳ͍͔Β ࢓૊Έ্(PE$MBTT͕๷͛ͳ͍ ͦ΋ͦ΋TUSVDUͩͬͨΒͲ͏͢Δʁ ։ൃ࣌͸ΦʔόʔϥΠυશ෦Ͱ͖͚ͨͲ อक࣌ʹ௥Ճͨ͠΋ͷͷΦʔόʔϥΠυ ๨ΕͨΒͲ͏͢Δʁ
  7. ͳͥ۩৅ʹґଘ͢Δͷ͸ѱ͍จ໌͔ class SomeComponent { func a() {} func b() {}

    } final class ObjectA { let component: SomeComponent init(component: SomeComponent) { self.component = component } func doSomething() { component.a() } } final class ObjectB { let component: SomeComponent init(component: SomeComponent) { self.component = component } func doSomething() { component.b() } } 0CKFDU"͕ࣗ෼ʹෆඞཁͳ 4PNF$PNQPOFOUC Λ஌ͬͯΔ͠ 0CKFDU#΋ࣗ෼ʹෆඞཁͳ 4PNF$PNQPOFOUB Λ஌͍ͬͯΔ 4PNF$PNQPOFOUࣗମͷӨڹൣғ͕ ඇৗʹ޿ͯ͘อकੑ͕Լ͕Δ 0CKFDU"ͱ0CKFDU#΋ ࣗ෼ͨͪͲ͜·Ͱ஌Δඞཁ͕͋Δ͔ Θ͔Γʹ͍͘ͷͰอकੑ͕Լ͕Δ
  8. ͳͥ۩৅ʹґଘ͢Δͷ͸ѱ͍จ໌͔ class SomeComponent { func a() {} func b() {}

    } final class ObjectA { let component: SomeComponent func doSomething() { component.a() } } final class ObjectB { } func test_doSomethingA() { final class MockComponent: SomeComponent { override func a() { // } override func b() { XCTFai() } } let object = ObjectA(component: MockComponent()) object.doSomething() } B ͚ͩΦʔόʔϥΠυ͢Ε͹͍͍͸ͣͳͷʹɺ C ΋ΦʔόʔϥΠυ͠ͳͪ͘Ό͍͚ͳ͍
  9. ந৅ protocol SomeProtocol { func add1(int: Int) -> Int }

    ۩৅ final class A: SomeProtocol { func add1(int: Int) -> Int { return int + 1 } } struct B: SomeProtocol { func add1(int: Int) -> Int { return int * 2 } } enum C: SomeProtocol { func add1(int: Int) -> Int { return 0 } }
  10. Ԡ༻ͯ͠ΈΔͱ protocol ComponentProtocol { func f() } final class SomeObject

    { let component: ComponentProtocol init(component: ComponentProtocol) { self.component = component } func doSomething() { component.f() } } final class SomeComponent { var p: Int = 0 } extension SomeComponent: ComponentProtocol { func f() {} }
  11. 4XJGU6*ʹద༻ͯ͠ΈΔͱ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct SomeView: View { @StateObject var object: SomeObject var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 }
  12. 4XJGU6*ʹద༻ͯ͠ΈΔͱ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView: View { @StateObject var object: SomeObject var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } Property type 'SomeObject' does not ...
  13. 4XJGU6*ʹద༻ͯ͠ΈΔͱ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView: View { @StateObject var object: SomeObject var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } Property type 'SomeObject' does not match that of the 'wrappedValue' property of its wrapper type 'StateObject'
  14. ํ๏ɿδΣωϦΫεΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView: View { @StateObject var object: SomeObject var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } Property type 'SomeObject' does not ...
  15. ํ๏ɿδΣωϦΫεΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView<Object: SomeObject>: View { @StateObject var object: Object var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } δΣωϦΫεΛೖΕͯ͋͛Ε͹ 4UBUF0CKFDUʹ୅ೖͰ͖Δ
  16. ํ๏ɿδΣωϦΫεΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView<Object: SomeObject>: View { @StateObject var object: Object var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } return MyView(object: Counter())
  17. ํ๏ɿδΣωϦΫεΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView<Object: SomeObject>: View { @EnvironmentObject var object: Object var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } return MyView<Counter>() 4UBUF0CKFDU΍0CTFSWFE0CKFDUͷ ৔߹͸໰୊ͳ͍͕ɺ &OWJSPONFOU0CKFDUͷ৔߹͸ ΠχγϟϥΠβͰδΣωϦΫε͕ ղܾͰ͖ͳ͍ͷͰɺ ໌ࣔతʹδΣωϦοΫܕΛ ॻ͘ඞཁ͕͋Δ
  18. ํ๏ɿδΣωϦΫεΛ࢖͏ protocol ObjectA: ObservableObject { var int: Int { get

    } } protocol ObjectB: ObservableObject { var string: String { get } } struct MyView<A: ObjectA, B: ObjectB>: View { @EnvironmentObject var a: A @EnvironmentObject var b: B var body: some View { Text("\(a.int), \(b.string)") } } final class MyObjectA: ObjectA { @Published var int = 0 } final class MyObjectB: ObjectB { @Published var string = "" } return MyView<MyObjectA, MyObjectB>() &OWJSPONFOU0CKFDUͷґଘ͕૿͑Δͱ δΣωϦοΫܕ΋૿͍͑ͯ͘
  19. ํ๏ɿδΣωϦΫεΛ࢖͏ protocol ObjectA: ObservableObject { var int: Int { get

    } } protocol ObjectB: ObservableObject { var string: String { get } } struct MyView<A: ObjectA, B: ObjectB>: View { @EnvironmentObject var a: A @EnvironmentObject var b: B var body: some View { Text("\(a.int), \(b.string)") } } protocol ComponentA {} final class MyObjectA<A: ComponentA>: ObjectA { @ObservedObject var a: A @Published var int = 0 } protocol ComponentB {} final class MyObjectB<B: ComponentB>: ObjectB { @ObservedObject var b: B @Published var string = "" } struct Component: ComponentA, ComponentB {} return MyView<MyObjectA<Component>, MyObjectB<Component>>() &OWJSPONFOU0CKFDUͷґଘʹ΋ δΣωϦΫε͕࢖ΘΕΔͱ δΣωϦοΫܕͷωετͰ͞ΒʹΧΦε
  20. ํ๏ɿܕফڈΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView<Object: SomeObject>: View { @EnvironmentObject var object: Object var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } return MyView<Counter>()
  21. ํ๏ɿܕফڈΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView: View { @EnvironmentObject var object: AnySomeObject var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } return MyView() δΣωϦοΫܕ͕ͳ͘ͳͬͯεοΩϦ ܕফڈͨ͠ܕΛ࢖͏ͷͰ ۩৅Λ஌Δඞཁ͕ͳ͍··
  22. ํ๏ɿܕফڈΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } private class SomeObjectBox { var int: Int { fatalError() } var objectWillChange: ObservableObjectPublisher { fatalError() } } 4UFQ ܧঝͤ͞ΔͨΊͷμϛʔΫϥεΛ࡞Δɻ ͦͷࡍΦʔόʔϥΠυͤ͞ΔͨΊͷϓϩύςΟʔ ΍ϝιουΛ࡞ΓɺGBUBM&SSPS Λฦ͢ɻ ·ͨ͜͜Ͱফڈ͍ͨ͠ܕ΋༧Ίఆ͓ٛͯ͘͠ʢࠓ ճͷ৔߹͸PCKFDU8JMM$IBOHFͷܕͰ͋Δ 0CTFSWBCMF0CKFDU0CKFDU8JMM$IBOHF1VCMJTIFS Λ0CTFSWBCMF0CKFDU1VCMJTIFSʹݻఆ͢Δʣ
  23. ํ๏ɿܕফڈΛ࢖͏ private final class AnySomeObjectBox<Object: SomeObject>: SomeObjectBox where Object.ObjectWillChangePublisher ==

    ObservableObjectPublisher { private let object: Object fileprivate init(_ object: Object) { self.object = object } override var int: Int { return object.int } override var objectWillChange: ObservableObjectPublisher { return object.objectWillChange } } 4UFQ μϛʔΫϥεΛܧঝͤ͞ɺܧঝͨ͠ࢠΫϥεʹ QSPUPDPM४ڌͨ͠δΣωϦοΫܕΛೖΕɺফڈ ͍ͨ͠ܕͷ੍ݶΛೖΕ͓ͯ͘ɻ ͦͷࡍɺμϛʔΫϥεͷશͯͷϓϩύςΟʔͱ ϝιουΛΦʔόʔϥΠυ͠ɺೖΕͬࢠͷ֘౰ ϓϩύςΟʔ΍ϝιουΛฦ͢ɻ
  24. ํ๏ɿܕফڈΛ࢖͏ final class AnySomeObject: SomeObject { private let object: SomeObjectBox

    fileprivate init<O: SomeObject>(_ object: O) where O.ObjectWillChangePublisher == ObservableObjectPublisher { self.object = AnySomeObjectBox(object) } var int: Int { return object.int } var objectWillChange: ObservableObjectPublisher { return object.objectWillChange } } 4UFQ QSPUPDPMʹ४ڌͨ͠ܕফڈίϯςφΫϥεΛ࡞ Γɺ಺෦ͰμϛʔΫϥεΛ࣋ͨͤΔɻ ࣮ࡍͷੜ੒࣌͸δΣωϦοΫܕΛ࣋ͬͨࢠΫϥε Ͱੜ੒ͯ࣋ͨͤ͠Δɻ ͦͯ͠QSPUPDPMͷ֤ϓϩύςΟʔ΍ϝιουΛ ೖΕͬ͜ͷͰฦ͢ɻ ͜͏͢Ε͹δΣωϦοΫܕΛ࣋ͨͳ͍μϛʔΫϥ ε͔͠ͳ͍ͷͰδΣωϦΫε͕ඞཁͳ͍ʀͰ΋Ϋ ϥεͷܧঝͱΦʔόʔϥΠυʹΑΓͪΌΜͱδΣ ωϦοΫܕΛ࣋ͬͨࢠΫϥεͷৼΔ෣͍Λ͢Δɻ
  25. ํ๏ɿܕফڈΛ࢖͏ extension SomeObject where Self.ObjectWillChangePublisher == ObservableObjectPublisher { func eraseToAnySomeObject()

    -> AnySomeObject { return AnySomeObject(self) } } 4UFQ ར༻࣌ͷ࢖͍΍͢͞Λߟ͑ͯɺܕফڈͨ͠ίϯ ςφΫϥε΁ͷੜ੒ϝιουΛ࡞Δɻ
  26. ํ๏ɿܕফڈΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView: View { @EnvironmentObject var object: AnySomeObject var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } return MyView()
  27. ํ๏ɿܕফڈΛ࢖͏ protocol SomeObject: ObservableObject { var int: Int { get

    } } struct MyView: View { @EnvironmentObject var object: AnySomeObject var body: some View { Text("\(object.int)") } } final class Counter: SomeObject { @Published var int = 0 } return MyView() .environmentObject(Counter().eraseToAnySomeObject()) &OWJSPONFOU0CKFDUͳͷͰɺ Ͳ͜Ͱ஫ೖͯ͠΋େৎ෉ʂ
  28. δΣωϦΫε ܕফڈ ϝϦοτ ໘౗ͳ࢓૊͕ཁΒͳ͍ͷͰ ࣮૷ָ͕ ґଘͷ࣮ଶΛؾʹ͠ͳͯ͘ ࡁΉͷͰɺδΣωϦΫε͕ ཁΒͳ͍͔Βίʔυࣗମ͕ εοΩϦ͢Δ σϝϦοτ

    ґଘ͕૿͑ͨΓɺґଘࣗମ ΋ߋʹδΣωϦΫεΛ࢖͏ ͱॻ͖΋ಡΈ΋͠ʹ͍͘ ܕফڈͷ࢓૊ࣗମΛ࡞Δͷ ͕ඇৗʹ໘౗ ࢖͏৔໘ w 4UBUF0CKFDU΍ 0CTFSWFE0CKFDU w ґଘ͕গͳ͍ w δΣωϦΫεͷωετ͕ ਂ͘ͳ͍ w &OWJSPONFOU0CKFDUͷ ґଘ͕ଟ͍ w &OWJSPONFOU0CKFDUͷ ґଘͷδΣωϦΫεͷω ετ͕ਂ͍