Slide 1

Slide 1 text

iOSDC Japan 2019 2019 9/6(Fri) 13:30~14:00 TrackA Swift ΫϦʔϯίʔυ Ξυϕϯνϟʔɹ ~೔ʑͷۤ೰Λ৐Γӽ͑ɺ͔֬ͳબ୒Λ͢ΔͨΊʹ~ ը૾ͷϦϯΫ

Slide 2

Slide 2 text

VISITS Technologiesגࣜձࣾ shiz@stzn3 shiz(ͣ͠) @shiz stzn(shiz) ։ൃܦݧݴޠ: Swift Kotlin Javascript PHP C# Java Go…

Slide 3

Slide 3 text

ΫϦʔϯ ίʔυ ը૾ͷϦϯΫ

Slide 4

Slide 4 text

ΫϦʔϯίʔυ ͱ͸ʁ

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

ୡਓͨͪᐌ͘ ͻͱͭͷ͜ͱΛߦͬͯҙਤ͕໌֬ ෺ޠΛಡΉΑ͏ʹಡΊΔ ΤϨΨϯτͰޮ཰͕ྑ͍ ࣄ࣮ʹଈ͍ͯ͠ͳΕ͹ͳΒͳ͍ ҙຯΛ໊࣋ͬͨલ͕͋Δ ґଘੑ͸࠷௿ݶͰ໌֬ʹఆٛ͞Ε͍ͯΔ ಡΉͷʹେ͖ͳ౒ྗ͕ඞཁͳ͍ ڻ͖͕ͳ͍ ॏෳΛݮΒ͠ɺදݱྗΛߴΊɺ ୯७ͳந৅ԽΛૣ͍࣌ظʹ࡞Γ͋͛Δ খ͖͜͞ͱ͸ྑ͖͜ͱ ݪ࡞ऀҎ֎ͷਓʹ΋ಡΉ͜ͱ͕Ͱ͖ɺ ֦ுͰ͖Δίʔυ ৗʹ୭͔͕ؾ഑ΓΛ࣋ͬͯॻ͍͍ͯΔΑ͏ʹݟ͑Δ Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C. Martin(ஶ), ՖҪࢤੜ(༁)

Slide 7

Slide 7 text

Uncle Bobᐌ͘ ಡΈ΍͍͢ίʔυ Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C. Martin(ஶ), ՖҪࢤੜ(༁)

Slide 8

Slide 8 text

ը૾ͷϦϯΫ

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

Θ͔Γ΍͍͢ ҆શͰ͋Δ มߋʹڧ͍

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

ΫϦʔϯίʔυΛ ॻ͘ʹ͸ʁ

Slide 15

Slide 15 text

ը૾ͷϦϯΫ

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

δΣωϦΫε (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

Slide 18

Slide 18 text

Protocol ը૾ͷϦϯΫ

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Start With a Protocol ϓϩτίϧ͔Β࢝ΊΑ Protocol-Oriented Programming in Swift WWDC2015

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

use value types, then if you need polymorphism, make them conform to protocols. Avoid classes. https://twitter.com/cocoaphony/status/1104114233288151043 ϙϦϞʔϑΟζϜ ಉ໊͡લͷϝιουΛෳ਺ͷΫϥεͰ࢖༻Ͱ͖ΔΑ͏ʹ͠ɺͦͷϝ ιουΛ௨ͯ͠ɺ҉໧తʹෳ਺ͷΠϯελϯεͷಈ࡞Λ੾Γସ͑Δ ͜ͱ͕Ͱ͖ΔΑ͏ʹ͢Δ͜ͱ

Slide 26

Slide 26 text

…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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

۩ମతղܾ ໰୊ δΣωϦοΫͳղܾ ໰୊ ໰୊ ໰୊ ݸʑͷ۩ମత໰୊Λղܾ͠ δΣωϦοΫͳղܾࡦΛநग़ ۩ମతղܾ ໰୊

Slide 29

Slide 29 text

ը૾ͷϦϯΫ

Slide 30

Slide 30 text

struct Item: Codable { let id: Int let name: String } struct User: Codable { let id: Int let name: String } struct Client { let baseURL = URL(string: “…”)! }

Slide 31

Slide 31 text

extension Client { func fetchItem(id: Int, completion: @escaping (Result) -> 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() } }

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

extension Client { func fetchUser(id: Int, completion: @escaping (Result) -> 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() } }

Slide 36

Slide 36 text

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() } }

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

extension Client { func fetch( _: Model.Type, } } Client().fetch(id: 1) { … } Client().fetch(id: 1) { (result: Result) … }

Slide 39

Slide 39 text

Client().fetch(id: 1) { (result: Result) … }

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

extension Client { func fetch( …) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent(“???”) .appendingPathComponent(“\(id)") ) } }

Slide 42

Slide 42 text

protocol Fetchable: Decodable { static var apiBase: String { get } }

Slide 43

Slide 43 text

extension User: Fetchable { static var apiBase: String { return "users" } } extension Item: Fetchable { static var apiBase: String { return “items" } }

Slide 44

Slide 44 text

let urlRequest = URLRequest(url: baseURL .appendingPathComponent(Model.apiBase)

Slide 45

Slide 45 text

extension User: Fetchable { static var apiBase: String { return "users" } } extension Item: Fetchable { static var apiBase: String { return “items" } }

Slide 46

Slide 46 text

Retroactive Modeling ݩͷιʔεʹΞΫηε͢ Δ͜ͱ͕Ͱ͖ͳ͍ܕͰ΋ ػೳΛ֦ு͢Δ͜ͱ͕Ͱ ͖Δೳྗ The swift programming language swift 5.1

Slide 47

Slide 47 text

ը૾ͷϦϯΫ

Slide 48

Slide 48 text

extension Client { func fetch( _: Model.Type, id: Int, completion: @escaping (Result) -> Void) { let urlRequest = URLRequest(url: baseURL .appendingPathComponent(Model.apiBase) .appendingPathComponent(“\(id)") ) let session = URLSession.shared session.dataTask(with: urlRequest) { … }.resume() } }

Slide 49

Slide 49 text

protocol URLSessionProtocol { func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask } extension URLSession: URLSessionProtocol {}

Slide 50

Slide 50 text

struct Client { let session: URLSessionProtocol init(session: URLSessionProtocol) { self.session = session } }

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

protocol URLSessionProtocol { func dataTask( with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask }

Slide 53

Slide 53 text

protocol Transport { func send(request: URLRequest, completion: @escaping (Result) -> Void) } URLRequest -> Result

Slide 54

Slide 54 text

struct Client { let transport: Transport init(transport: Transport) { self.transport = transport } } func fetch( transport.send(request: urlRequest) { … return try decoder.decode(Model.self, from: result.get())

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

extension URLSession: Transport { func send(request: URLRequest, completion: @escaping (Result) -> Void) { } } Retroactive Modeling

Slide 57

Slide 57 text

struct Client { init( transport: Transport = URLSession.shared ) { … } } let client = Client()

Slide 58

Slide 58 text

sendϝιου͕།Ұͷػೳ Foundation಺ͷܕͷΈΛ࢖༻ URLRequest, Result Transport

Slide 59

Slide 59 text

struct TransportWithToken: Transport { let base: Transport let token: String func send(request: URLRequest, completion: @escaping (Result) -> Void) { var newRequest = request newRequest.addValue(token, forHTTPHeaderField: "token") base.fetch(request: newRequest, completion: completion) } }

Slide 60

Slide 60 text

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(…)) …

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

protocol Transport { func fetch(request: Request) } ※associatedtype·ͨ͸SelfΛ࢖͏Protocol͸ δΣωϦΫεͷ੍໿ͱͯ͠࢖༻Ͱ͖Δ͕ ௚઀ܕͱͯ͠࢖༻Ͱ͖ͳ͘ͳΔ PAT(Protocol With AssociatedType)※

Slide 64

Slide 64 text

protocol Transport { func fetch(request: R) }

Slide 65

Slide 65 text

extension Client { func multiFetch(requests: [R]) { for request in requests { transport.send(request: request) } } }

Slide 66

Slide 66 text

͜ͷProtocol͸ ഑ྻͱͯ͠࢖͏͔ʁ

Slide 67

Slide 67 text

protocol Request { func makeURLRequest() throws -> URLRequest } protocol Transport { func send(request: Request, completion: @escaping (Result) -> Void) }

Slide 68

Slide 68 text

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 } }

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

ը૾ͷϦϯΫ

Slide 72

Slide 72 text

ը૾ͷϦϯΫ

Slide 73

Slide 73 text

struct Item: Codable { let id: Int let name: String } struct User: Codable { let id: Int let name: String }

Slide 74

Slide 74 text

let itemId = 1 client.fetch(User.Type, id: itemId) { … } ❌

Slide 75

Slide 75 text

struct Item: Codable { let id: String let name: String } func fetch( _: Model.Type, id: Int, … ❌

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

protocol Identifiable: Codable, Hashable { associatedtype Value var value: Value { get } init(value: Value) }

Slide 78

Slide 78 text

let ids: [Identifiable] = [ User.ID(1), Item.ID(“item-1”) ] PAT(Protocol With AssociatedType)

Slide 79

Slide 79 text

͜ͷProtocol͸ ഑ྻͱͯ͠࢖͏͔ʁ

Slide 80

Slide 80 text

protocol Fetchable: Decodable { associatedtype ID: Identifiable }

Slide 81

Slide 81 text

struct Item: Codable { struct ID: Identifiable { let value: String } let id: ID } struct User: Codable { struct ID: Identifiable { let value: Int } let id: ID }

Slide 82

Slide 82 text

func fetch( _: Model.Type, id: Model.ID, …

Slide 83

Slide 83 text

struct Item: Codable { struct ID: Identifiable { let value: String } let id: ID } struct User: Codable { struct ID: Identifiable { let value: Int } let id: ID }

Slide 84

Slide 84 text

func fetch( _: Model.Type, id: Model.ID, …

Slide 85

Slide 85 text

struct Item: Codable { struct ID: Identifiable { let value: String } let id: ID } struct User: Codable { struct ID: Identifiable { let value: Int } let id: ID }

Slide 86

Slide 86 text

struct Identifier where Value: Codable & Hashable { let value: Value init(_ value: Value) { self.value = value } } extension Identifier: Codable, Hashable { … }

Slide 87

Slide 87 text

protocol Fetchable: Decodable { static var apiBase: String { get } associatedtype IDType: Codable & Hashable typealias ID = Identifier var id: ID { get } }

Slide 88

Slide 88 text

struct Item: Codable { typealias ID = Identifier var id: ID { get } } struct User: Codable { typealias ID = Identifier var id: ID { get } }

Slide 89

Slide 89 text

func fetch( _: Model.Type, id: Model.ID, …

Slide 90

Slide 90 text

protocol Fetchable: Decodable { associatedtype IDType: Codable typealias ID = Identifier var id: ID { get } static var apiBase: String { get } } ࣝผࢠ(Identifier)Λ࣋ͭ + API͔Βσʔλऔಘ

Slide 91

Slide 91 text

struct Item: Codable { typealias = Identifier let id: ID } struct User: Codable { typealias = Identifier let id: ID }

Slide 92

Slide 92 text

protocol Identifiable: Codable { associatedtype IDType: Codable & Hashable typealias ID = Identifier var id: ID { get } }

Slide 93

Slide 93 text

protocol Fetchable: Identifiable { static var apiBase: String { get } }

Slide 94

Slide 94 text

protocol Identifiable: Codable { associatedtype IDType: Codable & Hashable typealias ID = Identifier var id: ID { get } } protocol Fetchable: Identifiable { static var apiBase: String { get } } ࣝผࢠ(Identifier)Λ࣋ͭ IDΛ࢖ͬͯAPI͔Βσʔλऔಘ

Slide 95

Slide 95 text

struct Item: Identifiable { typealias IDType = String let id: ID … } struct User: Identifiable { typealias IDType = Int let id: ID … }

Slide 96

Slide 96 text

POP Protocol Oriented Programming Obsessed ※ Obsessed औΓጪ͔ΕΔ

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

۩ମతղܾ -> δΣωϦοΫ ղܾํ๏Λཧղͯ͠બ୒ Θ͔Γ΍͍͢ɺ҆શͰ͋Δ มߋʹڧ͍

Slide 99

Slide 99 text

ը૾ͷϦϯΫ

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

Ձ஋ମܥ˞͕ͦ͜໨ඪ Ͱ͋Γɺ͜ͷຊͷ ςʔϚͩͬͨͷͰ͢ɻ Clean Code ΞδϟΠϧιϑτ΢ΣΞୡਓͷٕ Robert C. Martin(ஶ), ՖҪࢤੜ(༁) ※ࢥߟɾߦಈ͢Δࡍͷ͋Δಛఆͷ ྖҬʹ͓͚ΔՁ஋؍ɾ൑அج४

Slide 103

Slide 103 text

ը૾ͷϦϯΫ

Slide 104

Slide 104 text

No content

Slide 105

Slide 105 text

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 ิ଍هࣄ

Slide 106

Slide 106 text

͋Γ͕ͱ͏͍͟͝·ͨ͠ ը૾ͷϦϯΫ

Slide 107

Slide 107 text

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) ࢀߟϦϯΫू

Slide 108

Slide 108 text

Modern Swift API Design The swift programming language swift 5.1 Exrtesions Protocols are more than Bags of Syntax Configurable types in Swift ࢀߟϦϯΫू