swift_clean_code.pdf

Cc8ec9594b83b5dc3c7eef345c05dc8d?s=47 shiz
September 06, 2019

 swift_clean_code.pdf

Presentation about swift clean code

Cc8ec9594b83b5dc3c7eef345c05dc8d?s=128

shiz

September 06, 2019
Tweet

Transcript

  1. iOSDC Japan 2019 2019 9/6(Fri) 13:30~14:00 TrackA Swift ΫϦʔϯίʔυ Ξυϕϯνϟʔɹ

    ~೔ʑͷۤ೰Λ৐Γӽ͑ɺ͔֬ͳબ୒Λ͢ΔͨΊʹ~ ը૾ͷϦϯΫ
  2. VISITS Technologiesגࣜձࣾ shiz@stzn3 shiz(ͣ͠) @shiz stzn(shiz) ։ൃܦݧݴޠ: Swift Kotlin Javascript

    PHP C# Java Go…
  3. ΫϦʔϯ ίʔυ ը૾ͷϦϯΫ

  4. ΫϦʔϯίʔυ ͱ͸ʁ

  5. Robert C. Martin (Uncle Bob) $MFBO$PEF")BOECPPLPG"HJMF4PGUXBSF$SBGUTNBOTIJQ $MFBO$PEFɹΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ https://blog.cleancoder.com

  6. ୡਓͨͪᐌ͘ ͻͱͭͷ͜ͱΛߦͬͯҙਤ͕໌֬ ෺ޠΛಡΉΑ͏ʹಡΊΔ ΤϨΨϯτͰޮ཰͕ྑ͍ ࣄ࣮ʹଈ͍ͯ͠ͳΕ͹ͳΒͳ͍ ҙຯΛ໊࣋ͬͨલ͕͋Δ ґଘੑ͸࠷௿ݶͰ໌֬ʹఆٛ͞Ε͍ͯΔ ಡΉͷʹେ͖ͳ౒ྗ͕ඞཁͳ͍ ڻ͖͕ͳ͍ ॏෳΛݮΒ͠ɺදݱྗΛߴΊɺ

    ୯७ͳந৅ԽΛૣ͍࣌ظʹ࡞Γ͋͛Δ খ͖͜͞ͱ͸ྑ͖͜ͱ ݪ࡞ऀҎ֎ͷਓʹ΋ಡΉ͜ͱ͕Ͱ͖ɺ ֦ுͰ͖Δίʔυ ৗʹ୭͔͕ؾ഑ΓΛ࣋ͬͯॻ͍͍ͯΔΑ͏ʹݟ͑Δ Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C. Martin(ஶ), ՖҪࢤੜ(༁)
  7. Uncle Bobᐌ͘ ಡΈ΍͍͢ίʔυ Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C. Martin(ஶ), ՖҪࢤੜ(༁)

  8. ը૾ͷϦϯΫ

  9. ΫϦʔϯίʔυಓ৔ “զʑࣗ਎͕௕͍ؒɺ͔֬Ͱ͋Δ ͱߟ͖͑ͯͨ͜ͱͰ͢ɻԿे೥ʹ ౉ΔܦݧͱτϥΠΞϯυΤϥʔͷ ੵΈॏͶ͔ΒֶΜͩ΋ͷͰ͢ɻ” Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C.

    Martin(ஶ), ՖҪࢤੜ(༁)
  10. Θ͔Γ΍͍͢ ҆શͰ͋Δ มߋʹڧ͍

  11. Θ͔Γ΍͍͢ ҆શͰ͋Δ มߋʹڧ͍ ಡΈ΍͢͞ɺཧղͷ͠΍͢͞ etc όά͕ൃੜͮ͠Β͍ɺςετ͕༰қ etc मਖ਼࣌ͷӨڹൣғ͕ݶఆత ػೳ௥Ճ࣌ͷطଘίʔυ΁ͷӨڹ౓খ etc

  12. Kickstarter ߏ଄ͷ౷Ұ(ViewModel Input,Output etc) SingletonͰDI؅ཧ(Environment) ը໘ભҠͷू໿؅ཧ(Router) Playgroundۦಈ։ൃ

  13. Kickstarter ios-oss https://github.com/kickstarter/ios-oss POINT•FREE https://www.pointfree.co/ objc.io iOS at Kickstarter https://talk.objc.io/collections/ios-at-kickstarter

    How to Control the World https://vimeo.com/291588126 Reactive view models, simplified https://www.youtube.com/watch?v=uTLG_LgjWGA Kickstarter ios-ossߏ੒ͷղઆ https://note.mu/yimajo/n/ne44c7945279a RxSwiftݚڀಡຊ3 ViewModelઃܭύλʔϯೖ໳ https://booth.pm/ja/items/1223536 Kickstarter-iOSͷViewModelͷ࡞Γํ͕΢Ϛ͔ͬͨ https://qiita.com/muukii0803/items/045b12405f7acff1a9fd
  14. ΫϦʔϯίʔυΛ ॻ͘ʹ͸ʁ

  15. ը૾ͷϦϯΫ

  16. δΣωϦοΫ ۩ମతͳσʔλܕʹ௚઀ ґଘͤͣ൚༻తʹར༻Մ ೳͳঢ়ଶ

  17. δΣωϦΫε (generics) GVODTXBQ5XP7BMVFT 5 @BJOPVU 5 @CJOPVU 5 GVODTXBQ5XP4USJOHT @BJOPVU4USJOH

    @CJOPVU4USJOH GVODTXBQ5XP%PVCMFT @BJOPVU%PVCMF @CJOPVU%PVCMF GVODTXBQ5XP*OUT @BJOPVU*OU @CJOPVU*OU https://docs.swift.org/swift-book/LanguageGuide/Generics.html
  18. Protocol ը૾ͷϦϯΫ

  19. Protocol ίϯύΠϧ࣌ δΣωϦΫεͷܕ੍໿ ܕ҆શɾ࠷దԽ Static ࣮ߦ࣌ ௚઀ܕͱͯ͠࢖༻ Dynamic Dispatch Dynamic

    func F<P: Protocol> (onlyOneType: P) let types:[Protocol] = [A(), B(), …]
  20. Protocol ίϯύΠϧ࣌ δΣωϦΫεͷܕ੍໿ ܕ҆શɾ࠷దԽ Static ࣮ߦ࣌ ௚઀ܕͱͯ͠࢖༻ Dynamic Dispatch Dynamic

    func F<P: Protocol> (onlyOneType: P) let types:[Protocol] = [A(), B(), …]
  21. Protocol ίϯύΠϧ࣌ δΣωϦΫεͷܕ੍໿ ܕ҆શɾ࠷దԽ Static ࣮ߦ࣌ ௚઀ܕͱͯ͠࢖༻ Dynamic Dispatch Dynamic

    func F<P: Protocol> (onlyOneType: P) let types:[Protocol] = [A(), B(), …]
  22. Start With a Protocol ϓϩτίϧ͔Β࢝ΊΑ Protocol-Oriented Programming in Swift WWDC2015

  23. Start With a Protocol For Everything? ͢΂ͯϓϩτίϧ͔Β࢝ΊΑ?

  24. if you want to write a generalized sort or binary

    search…Don’t start with a class. Start with a protocol. Protocol-Oriented Programming in Swift WWDC2015
  25. use value types, then if you need polymorphism, make them

    conform to protocols. Avoid classes. https://twitter.com/cocoaphony/status/1104114233288151043 ϙϦϞʔϑΟζϜ ಉ໊͡લͷϝιουΛෳ਺ͷΫϥεͰ࢖༻Ͱ͖ΔΑ͏ʹ͠ɺͦͷϝ ιουΛ௨ͯ͠ɺ҉໧తʹෳ਺ͷΠϯελϯεͷಈ࡞Λ੾Γସ͑Δ ͜ͱ͕Ͱ͖ΔΑ͏ʹ͢Δ͜ͱ
  26. …we considered a varied number of concrete types first. …we’re

    thinking about a kind of protocol that could join them all together. …it’s important to think of things as this way around. To start with some concrete types, and then try and unify them with a protocol. Swift Generics (Expanded) WWDC2018
  27. Don’t Literally Start With a Protocol Start with a concrete

    use cases Discover a need for generic code Try to compose solutions from existing protocols first Consider a generic type instead of a protocol Modern Swift API Design WWDC2019
  28. ۩ମతղܾ ໰୊ δΣωϦοΫͳղܾ ໰୊ ໰୊ ໰୊ ݸʑͷ۩ମత໰୊Λղܾ͠ δΣωϦοΫͳղܾࡦΛநग़ ۩ମతղܾ ໰୊

  29. ը૾ͷϦϯΫ

  30. struct Item: Codable { let id: Int let name: String

    } struct User: Codable { let id: Int let name: String } struct Client { let baseURL = URL(string: “…”)! }
  31. extension Client { func fetchItem(id: Int, completion: @escaping (Result<Item, Error>)

    -> Void) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent(“items") .appendingPathComponent("\(id)") ) let session = URLSession.shared session.dataTask(with: urlRequest) { (data, _, error) in if let error = error { completion(.failure(error)) } else if let data = data { let decoder = JSONDecoder() completion(Result { try decoder.decode(Item.self, from: data) }) } }.resume() } }
  32. extension Client { func fetchItem(id: Int, completion: @escaping (Result<Item, Error>)

    -> Void) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent("items") .appendingPathComponent("\(id)") ) } }
  33. extension Client { func fetchItem(id: Int, completion: @escaping (Result<Item, Error>)

    -> Void) { … let session = URLSession.shared session.dataTask(with: urlRequest) { … }.resume()
  34. extension Client { func fetchItem(id: Int, completion: @escaping (Result<Item, Error>)

    -> Void) { … let decoder = JSONDecoder() completion(Result { try decoder.decode(Item.self, from: data) }) } }
  35. extension Client { func fetchUser(id: Int, completion: @escaping (Result<User, Error>)

    -> Void) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent(“users") .appendingPathComponent("\(id)") ) let session = URLSession.shared session.dataTask(with: urlRequest) { (data, _, error) in if let error = error { completion(.failure(error)) } else if let data = data { let decoder = JSONDecoder() completion(Result { try decoder.decode(User.self, from: data) }) } }.resume() } }
  36. extension Client { func fetch Item(id: Int, completion: @escaping (Result<

    Item, Error>) -> Void) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent(" items") .appendingPathComponent("\(id)") ) let session = URLSession.shared session.dataTask(with: urlRequest) { (data, _, error) in if let error = error { completion(.failure(error)) } else if let data = data { let decoder = JSONDecoder() completion(Result { try decoder.decode( Item.self, from: data) }) } }.resume() } } extension Client { func fetch User(id: Int, completion: @escaping (Result< User, Error>) -> Void) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent(" users") .appendingPathComponent("\(id)") ) let session = URLSession.shared session.dataTask(with: urlRequest) { (data, _, error) in if let error = error { completion(.failure(error)) } else if let data = data { let decoder = JSONDecoder() completion(Result { try decoder.decode( User.self, from: data) }) } }.resume() } }
  37. extension Client { func fetch<Model: Decodable>( _: Model.Type, id: Int,

    completion: @escaping (Result<Model, Error>) -> Void) { … let decoder = JSONDecoder() completion(Result { try decoder.decode(Model.self, from: data) } }
  38. extension Client { func fetch<Model: Decodable>( _: Model.Type, } }

    Client().fetch(id: 1) { … } Client().fetch(id: 1) { (result: Result<User, Error>) … }
  39. Client().fetch(id: 1) { (result: Result<User, Error>) … }

  40. Client().fetch(User.self, id: 1) { … }

  41. extension Client { func fetch<Model: Decodable>( …) { let urlRequest

    = URLRequest(url: baseURL .appendingPathComponent(“???”) .appendingPathComponent(“\(id)") ) } }
  42. protocol Fetchable: Decodable { static var apiBase: String { get

    } }
  43. extension User: Fetchable { static var apiBase: String { return

    "users" } } extension Item: Fetchable { static var apiBase: String { return “items" } }
  44. let urlRequest = URLRequest(url: baseURL .appendingPathComponent(Model.apiBase)

  45. extension User: Fetchable { static var apiBase: String { return

    "users" } } extension Item: Fetchable { static var apiBase: String { return “items" } }
  46. Retroactive Modeling ݩͷιʔεʹΞΫηε͢ Δ͜ͱ͕Ͱ͖ͳ͍ܕͰ΋ ػೳΛ֦ு͢Δ͜ͱ͕Ͱ ͖Δೳྗ The swift programming language

    swift 5.1
  47. ը૾ͷϦϯΫ

  48. extension Client { func fetch<Model: Decodable>( _: Model.Type, id: Int,

    completion: @escaping (Result<Model, Error>) -> Void) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent(Model.apiBase) .appendingPathComponent(“\(id)") ) let session = URLSession.shared session.dataTask(with: urlRequest) { … }.resume() } }
  49. protocol URLSessionProtocol { func dataTask(with request: URLRequest, completionHandler: @escaping (Data?,

    URLResponse?, Error?) -> Void) -> URLSessionDataTask } extension URLSession: URLSessionProtocol {}
  50. struct Client { let session: URLSessionProtocol init(session: URLSessionProtocol) { self.session

    = session } }
  51. MockͷͨΊͷ Protocol Mock ΦϒδΣΫτͷ໛฿ Protocol δΣωϦοΫͳ໰୊ղܾ

  52. protocol URLSessionProtocol { func dataTask( with request: URLRequest, completionHandler: @escaping

    (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask }
  53. protocol Transport { func send(request: URLRequest, completion: @escaping (Result<Data, Error>)

    -> Void) } URLRequest -> Result<Data, Error>
  54. struct Client { let transport: Transport init(transport: Transport) { self.transport

    = transport } } func fetch<Model: Fetchable>( transport.send(request: urlRequest) { … return try decoder.decode(Model.self, from: result.get())
  55. extension URLSession: Transport { func send(request: URLRequest, completion: @escaping (Result<Data,

    Error>) -> Void) { dataTask(with: request) { (data, _, error) in if let error = error { return completion(.failure(error)) } if let data = data { completion(.success(data)) } }.resume() } }
  56. extension URLSession: Transport { func send(request: URLRequest, completion: @escaping (Result<Data,

    Error>) -> Void) { } } Retroactive Modeling
  57. struct Client { init( transport: Transport = URLSession.shared ) {

    … } } let client = Client()
  58. sendϝιου͕།Ұͷػೳ Foundation಺ͷܕͷΈΛ࢖༻ URLRequest, Result<Data, Error> Transport

  59. struct TransportWithToken: Transport { let base: Transport let token: String

    func send(request: URLRequest, completion: @escaping (Result<Data, Error>) -> Void) { var newRequest = request newRequest.addValue(token, forHTTPHeaderField: "token") base.fetch(request: newRequest, completion: completion) } }
  60. final class TestTransport: Transport { var requests: [Any] = []

    var responseData: [Data] init(responseData: [Data]) { self.responseData = responseData } func send(…) { requests.append(request) guard let data = responseData.removeFirst() else { return completion(.success(data)) } completion(.failure(…)) …
  61. protocol Request { associatedtype Response: Fetchable var baseURL: URL {

    get } var path: String { get } var httpMethod: HTTPMethod { get } var parameters: [String: Any] { get } }
  62. protocol Request { associatedtype Response: Fetchable var baseURL: URL {

    get } var path: String { get } var httpMethod: HTTPMethod { get } var parameters: [String: Any] { get } }
  63. protocol Transport { func fetch(request: Request) } ※associatedtype·ͨ͸SelfΛ࢖͏Protocol͸ δΣωϦΫεͷ੍໿ͱͯ͠࢖༻Ͱ͖Δ͕ ௚઀ܕͱͯ͠࢖༻Ͱ͖ͳ͘ͳΔ

    PAT(Protocol With AssociatedType)※
  64. protocol Transport { func fetch<R:Request>(request: R) }

  65. extension Client { func multiFetch<R: Request>(requests: [R]) { for request

    in requests { transport.send(request: request) } } }
  66. ͜ͷProtocol͸ ഑ྻͱͯ͠࢖͏͔ʁ

  67. protocol Request { func makeURLRequest() throws -> URLRequest } protocol

    Transport { func send(request: Request, completion: @escaping (Result<Data, Error>) -> Void) }
  68. struct TransportWithTokenRequest: Request { let base: Request let token: String

    func makeURLRequest() throws -> URLRequest { var newRequest = try base.makeURLRequest() newRequest.addValue(token, forHTTPHeaderField: "token") return newRequest } }
  69. protocol Request { func makeURLRequest() throws -> URLRequest } ʁ

  70. ՄೳͳݶΓ δΣωϦοΫʹ

  71. ը૾ͷϦϯΫ

  72. ը૾ͷϦϯΫ

  73. struct Item: Codable { let id: Int let name: String

    } struct User: Codable { let id: Int let name: String }
  74. let itemId = 1 client.fetch(User.Type, id: itemId) { … }

  75. struct Item: Codable { let id: String let name: String

    } func fetch<Model: Fetchable>( _: Model.Type, id: Int, … ❌
  76. protocol Identifiable: Codable, Hashable { associatedtype Value var value: Value

    { get } init(value: Value) } extension Identifiable { init(_ value: Value) { self.init(value: value) } }
  77. protocol Identifiable: Codable, Hashable { associatedtype Value var value: Value

    { get } init(value: Value) }
  78. let ids: [Identifiable] = [ User.ID(1), Item.ID(“item-1”) ] PAT(Protocol With

    AssociatedType)
  79. ͜ͷProtocol͸ ഑ྻͱͯ͠࢖͏͔ʁ

  80. protocol Fetchable: Decodable { associatedtype ID: Identifiable }

  81. struct Item: Codable { struct ID: Identifiable { let value:

    String } let id: ID } struct User: Codable { struct ID: Identifiable { let value: Int } let id: ID }
  82. func fetch<Model: Fetchable>( _: Model.Type, id: Model.ID, …

  83. struct Item: Codable { struct ID: Identifiable { let value:

    String } let id: ID } struct User: Codable { struct ID: Identifiable { let value: Int } let id: ID }
  84. func fetch<Model: Fetchable>( _: Model.Type, id: Model.ID, …

  85. struct Item: Codable { struct ID: Identifiable { let value:

    String } let id: ID } struct User: Codable { struct ID: Identifiable { let value: Int } let id: ID }
  86. struct Identifier<Model, Value> where Value: Codable & Hashable { let

    value: Value init(_ value: Value) { self.value = value } } extension Identifier: Codable, Hashable { … }
  87. protocol Fetchable: Decodable { static var apiBase: String { get

    } associatedtype IDType: Codable & Hashable typealias ID = Identifier<Self, IDType> var id: ID { get } }
  88. struct Item: Codable { typealias ID = Identifier<Item, String> var

    id: ID { get } } struct User: Codable { typealias ID = Identifier<User, Int> var id: ID { get } }
  89. func fetch<Model: Fetchable>( _: Model.Type, id: Model.ID, …

  90. protocol Fetchable: Decodable { associatedtype IDType: Codable typealias ID =

    Identifier<Self, IDType> var id: ID { get } static var apiBase: String { get } } ࣝผࢠ(Identifier)Λ࣋ͭ + API͔Βσʔλऔಘ
  91. struct Item: Codable { typealias = Identifier<Item, String> let id:

    ID } struct User: Codable { typealias = Identifier<User, String> let id: ID }
  92. protocol Identifiable: Codable { associatedtype IDType: Codable & Hashable typealias

    ID = Identifier<Self, IDType> var id: ID { get } }
  93. protocol Fetchable: Identifiable { static var apiBase: String { get

    } }
  94. protocol Identifiable: Codable { associatedtype IDType: Codable & Hashable typealias

    ID = Identifier<Self, IDType> var id: ID { get } } protocol Fetchable: Identifiable { static var apiBase: String { get } } ࣝผࢠ(Identifier)Λ࣋ͭ IDΛ࢖ͬͯAPI͔Βσʔλऔಘ
  95. struct Item: Identifiable { typealias IDType = String let id:

    ID … } struct User: Identifiable { typealias IDType = Int let id: ID … }
  96. POP Protocol Oriented Programming Obsessed ※ Obsessed औΓጪ͔ΕΔ

  97. Don’t Literally Start With a Protocol Modern Swift API Design

    WWDC2019 Start with a concrete use cases Discover a need for generic code Try to compose solutions from existing protocols first Consider a generic type instead of a protocol
  98. ۩ମతղܾ -> δΣωϦοΫ ղܾํ๏Λཧղͯ͠બ୒ Θ͔Γ΍͍͢ɺ҆શͰ͋Δ มߋʹڧ͍

  99. ը૾ͷϦϯΫ

  100. ΫϦʔϯίʔυΛ ॻ͖ଓ͚Δʹ͸ʁ

  101. ϘʔΠεΧ΢τͷنଇ ίʔυΛܧଓతʹ վળ͍ͯ͘͠ϓϩҙࣝ ίʔυ͸Կ౓΋ચ࿅͠ͳ͚Ε͹ͳ Γ·ͤΜ Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C.

    Martin(ஶ), ՖҪࢤੜ(༁)
  102. Ձ஋ମܥ˞͕ͦ͜໨ඪ Ͱ͋Γɺ͜ͷຊͷ ςʔϚͩͬͨͷͰ͢ɻ Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C. Martin(ஶ), ՖҪࢤੜ(༁)

    ※ࢥߟɾߦಈ͢Δࡍͷ͋Δಛఆͷ ྖҬʹ͓͚ΔՁ஋؍ɾ൑அج४
  103. ը૾ͷϦϯΫ

  104. None
  105. Configurable types ~ࣅͯඇͳΔίʔυΛڞ௨ʹѻ͏~ https://qiita.com/shiz/items/849e483338bd568cb6ab ۩ମతͳίʔυ͔Β࢝ΊΑ ~ࠓͷ໰୊Λղܾ͠ɺδΣωϦοΫͳίʔυΛݟग़͢~ https://qiita.com/shiz/items/5755a35887bcb7897464 มԽʹରԠ͠ຊ࣭Λ࠶ൃݟ͢Δ https://qiita.com/shiz/items/7b3bda7c2d84c5a83c0f Kickstarter͔ΒֶͿ

    ~ΑΓྑ͍։ൃΛ໨ࢦͨ͢Ίͷ࢓૊Έͱ͸ʁ~ https://qiita.com/shiz/items/79c7b39f94f32e548df3 Protocol͸ߏจͷೖΕ෺Ҏ্ͷଘࡏͰ͋Δ https://qiita.com/shiz/items/da71d547b59c757cca94 ิ଍هࣄ
  106. ͋Γ͕ͱ͏͍͟͝·ͨ͠ ը૾ͷϦϯΫ

  107. Kickstarter ios-oss Star with a protocol A mockery of protocols

    That’s not a number From Problem to Solution Protocol-Oriented Programming in Swift Swift Generics (Expanded) ࢀߟϦϯΫू
  108. Modern Swift API Design The swift programming language swift 5.1

    Exrtesions Protocols are more than Bags of Syntax Configurable types in Swift ࢀߟϦϯΫू