Share
Embed
Pure DI 101
DevSap勉強会 19/01/26
https://devsap.connpass.com/event/113851/
(前回)iOSアプリのアーキテクチャについて考えてたどり着いた結論
https://speakerdeck.com/yusuga/final-conclusion-of-the-ios-architecture
Yu Sugawara
January 26, 2019
Transcript
42 slides
-
DIͬͯ·͔͢ʁ • Dependency Injection (DI) • ґଘΦϒδΣΫτͷೖ • ͦͷଞͷΩʔϫʔυͱͯ͠DIίϯςφ •
iOS։ൃऀͳΒ99.9%ͬͨ͜ͱ͋ΔσβΠϯύλʔϯ -
ྫ • ڞ௨ͷఆٛ protocol Service { func requestUsers() } class
Twitter: Service { func requestUsers() { /* Twitter API */ } } class Instagram: Service { func requestUsers() { /* Instagram API */ } } -
DIͳ͠ class UsersViewController: UIViewController { let service = Twitter() //
UsersViewControllerTwitterʹґଘ͍ͯ͠Δ func reload() { service.requestUsers() } } let vc = UsersViewController() vc.reload() // ͠ґଘΛInstagramʹม͍͑ͨ߹ίʔυΛฤू͢Δඞཁ͕͋Δ -
ίϯετϥΫλɾΠϯδΣΫγϣϯ 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() } } -
ηολʔɾΠϯδΣΫγϣϯ class UsersViewController: UIViewController { var service: Service? func reload()
{ service?.requestUsers() } } let vc = UsersViewController() vc.service = Twitter() // ηολʔͰґଘΦϒδΣΫτΛೖ vc.reload() -
ηολʔɾΠϯδΣΫγϣϯͷར༻γʔϯ • ίϯετϥΫλɾΠϯδΣΫγϣϯͰ͖ͳ͍߹ • ྫ͑Storyboard༻࣌ let vc = UIStoryboard(name: "Main",
bundle: nil) .instantiateInitialViewController()! as! UsersViewController vc.service = Twitter() // ηολʔͰґଘΦϒδΣΫτΛೖ -
IBOutletηολʔɾΠϯδΣΫγϣϯͰղܾ͞ΕΔ class UsersViewController: UIViewController { // Storyboard(or Xib)͔ΒΠϯδΣΫγϣϯ͞ΕΔ @IBOutlet weak
var button: UIButton! } • ViewControllerΫϥεͷॳظԽޙɺґଘΦϒδΣΫτʢ্هͩͱ buttonʣ͕ࣗಈͰηολʔɾΠϯδΣΫγϣϯͰղܾ͞ΕΔ • ͳͷͰXcodeͰσϑΥϧτͰએݴ͞ΕΔܕUIButton!ʹͳͬ ͍ͯΔɻ -
IBOutletηολʔɾΠϯδΣΫγϣϯͰղܾ͞ΕΔ class UsersViewController: UIViewController { // Storyboard(or Xib)͔ΒΠϯδΣΫγϣϯ͞ΕΔ @IBOutlet weak
var button: UIButton! } • ͨͩ͠ɺxmlϑΝΠϧͱΫϥεͷએݴ͕Ұகͯ͠ͳ͔ͬͨΒϥ ϯλΠϜͰΫϥογϡ͢Δ • ޙड़͢ΔಈతDIίϯςφͷσϝϦοτ -
DIͷछྨ छྨ ґଘؔͷղܾํ๏ DI (≒ Pure DI, Vanilla DI) खಈ,
DIίϯςφΛ༻͠ͳ͍DI ಈతDIίϯςφ DIίϯςφͰ ಈత ʹґଘؔ Λղܾ ੩తDIίϯςφ DIίϯςφͰ ੩త ʹґଘؔ Λղܾ -
ओͳϥΠϒϥϦ (Swift) ϥΠϒϥϦ Github Star DIͷछผ Swinject 2,922 ಈతDIίϯςφ Cleanse
1,168 ಈతDIίϯςφ Dip 725 ಈతDIίϯςφ DIKit 242 ੩తDIίϯςφ Pure 186 Pure DI Github Star19/1/26࣌ -
Pure DIͱ • Pure DI by Mark Seemann • ಈతDIίϯςφΛ༻͠ͳ͍Ͱɺίϯ
ύΠϧ࣌ʹͯ͢ͷґଘؔΛ੩తʹ ղܾͤ͞ΔDI • ڧ͍੩తܕ͚ݴޠͷSwiftʹϚον ͍ͯ͠Δ -
Pure DI in Swift • Pure • ࡞ऀReactorKitͱಉ͡ • v1.0.0
• Pure DIΛΓ͘͢͢Δํ๏Λఏڙ͠ ͍ͯΔ͚ͩͳͷͰίʔυྔ͕গͳ͍ • ͨͩ͠αϯϓϧϓϩδΣΫτͳ͠… • ׆༻͢ΔͨΊʹҰൠతͳDIͷख๏ ͕Θ͔Βͳ͍ͱϐϯͱ͜ͳ͍͔͠ Εͳ͍ -
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() } } -
ಈత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() -
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) // ॳظԽ } -
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() } } -
Pure DI: Pure let factory = UsersViewController.Factory( dependency: UsersViewController.Dependency( service:
Twitter() // ґଘΦϒδΣΫτ ) ) // ͜ͷfactoryґଘؔΛղܾͨ͠FactoryΦϒδΣΫτ // ґଘؔΛղܾͨ͠UsersViewControllerΛੜͯ͠ฦ͢ let vc = factory.create() vc.reload() -
• 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() } } -
DI: खಈ let vc = UsersViewController( service: Twitter(), // ґଘΦϒδΣΫτ
userName: "yusuga" // ಈతʹܾΊ͍ͨ ) vc.reload() -
ಈత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() -
ҙ: 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 // userNameStringܕ͕ͩίϯύΠϧ௨Δ ) // ܕ͕ҰகͤͣґଘΛղܾͰ͖ͳ͍߹nil͕ฦΔ vc.reload() -
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) } -
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) } /* ҎԼলུ */ } -
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() -
Pure͕ఏڙ͍ͯ͠Δͷ1 • ࣄલʹܾఆͰ͖ΔґଘΦϒδΣΫτ(Dependency)Λղܾͨ͠ FactoryΫϥε let factory = UsersViewController.Factory( dependency: UsersViewController.Dependency(
service: Twitter() ) ) // ͜ͷfactoryґଘؔΛղܾͨ͠FactoryΦϒδΣΫτ -
·ͱΊ • DIͳ͠ɺDI, ಈతDIίϯςφͷ࠷ऴతʹੜ͞ΕΔͷಉ͡ ͰɺͦͷաఔͰґଘؔΛͲ͏͢Δ͔ͱ͍͏ • ಈతDIίϯςφ࣮ϛεϥϯλΠϜͰ͔͠ݕͰ͖ͳ͍ͱ ͍͏σϝϦοτ͕͋Δ • ָ࣮ͨͩ
• SwinjectߴػೳͰ΄ͱΜͲͷύλʔϯΛղܾͰ͖Δ -
·ͱΊ • ࠓճհ͍ͯ͠·ͤΜ͕ɺDIKitಈతDIίϯςφͷϥϯλΠϜ Ͱ͔͠ϛεΛݕͰ͖ͳ͍෦Λิͬͨ ੩త DIίϯςφΛ࠾༻ • Build phasesͰιʔε༰͔ΒDIίϯςφΫϥεϑΝΠϧΛੜ Ͱ͖ΔShell
scriptΛఏڙ • ͨͩɺv0.4.0ʢ19/01/26࣌ʣͱ͍͏͜ͱ͔͋ͬͯɺ·ͩ एׯͷػೳෆײ͋Δ -
࣍ͷؔ৺ • ը໘ؒɺը໘ભҠͷґଘΛͲ͏ͬͯ ղܾ͢Δ͔ • RxSwiftCommunity/RxFlow • Reactive Flow Coordinator
pattern • ͪ͜Β2.5ਓ݄ͷϓϩδΣΫτʹ࣮ઓ ೖͯ͠खԠ͑͋ͬͨʢPure͚ͩͰ ղܾͰ͖ͳ͍͕͋ͬͨͷͰҰॹʹ ಋೖʣ