Slide 1

Slide 1 text

υΩϡϝϯτΈ͍ͨͳ API Client ΛٻΊͯ 2015/06/20 ԣ඿΁ͳͪΐ͜iOS #20 @dealforest Toshihiro Morimoto

Slide 2

Slide 2 text

ࣗݾ঺հ

Slide 3

Slide 3 text

Crying DFTDebugScreenshot cocoapods-browser Tuna

Slide 4

Slide 4 text

https://github.com/dealforest/Tuna

Slide 5

Slide 5 text

by AlcatrazTour

Slide 6

Slide 6 text

ܦҢ

Slide 7

Slide 7 text

ܦҢ

Slide 8

Slide 8 text

·ͩಀ͛ΕΔ…

Slide 9

Slide 9 text

ܦҢ

Slide 10

Slide 10 text

Φϫλ/(^o^)\

Slide 11

Slide 11 text

ͱ͍͏Θ͚ͰLT͢Δ͜ͱʹͳΓ·ͨ͠

Slide 12

Slide 12 text

஫ҙ ͜ΕΑΓઌ͸ࢲݟͰຬͪᷓΕ͍ͯΔͷͰɹ ͦΕΛ౿·্͑ͨͰฉ͍ͯ௖͚Δͱ޾͍Ͱ ͢

Slide 13

Slide 13 text

API Client ࢙ 1. UIKitظ (NSURLConnection, NSURLSession ౳) 2. ϥΠϒϥϦظ (AFNetwork, Alamofire ౳) 3. UIKit + αظ (؆୯ͳ Wrapper Λࣗ෼Ͱॻ͘)

Slide 14

Slide 14 text

࠷௿ݶͳχʔζ • ௨৴ϨΠϠʔΛू໿Խ͍ͨ͠ • Object Mapping ͸΄͍͠ • υΩϡϝϯτΛॻ͔ͣʹɺ͍͔ʹυΩϡ ϝϯτͬΆ͘͢Δ͔

Slide 15

Slide 15 text

͍͔ʹυΩϡϝϯτͬΆ͘͢Δ͔

Slide 16

Slide 16 text

Why document?

Slide 17

Slide 17 text

Կ౓΋ API Client Λ࢖͖ͬͯ·͕ͨ͠ ਺िؒ΋ͨͭͱ API ࢓༷͕ هԱͷ൴ํʹ๨٫ͯ͠͠·͍·͢

Slide 18

Slide 18 text

͞Θ͍ͬͯͳ͍ͱಛʹૣ͍Ͱ͢ΑͶ

Slide 19

Slide 19 text

ͦ͏ͳͬͨ৔߹ɺͲΕ͚ͩஸೡʹ ίʔυΛॻ͍ͨͱͯ͠΋ API ͷ ࢓༷·Ͱ͸Θ͔Βͳ͍Ͱ͠ΐ͏

Slide 20

Slide 20 text

ͳͷͰ࣮ࡍʹϦϑΝϨϯεΛΈͨΓ API Λୟ͍ͨΓ ςετίʔυͰ֬ೝͨ͠Γ ͳͲͳͲ

Slide 21

Slide 21 text

ͦΜͳ࣌ͱ͋ΔൃදΛΈͯ ײಈ͠·ͨ͠

Slide 22

Slide 22 text

https://speakerdeck.com/ishkawa/introducing-apikit

Slide 23

Slide 23 text

ίʔυ͕ͦͷ·· υΩϡϝϯτͬΆ͘ಡΊΔͷͰ͢

Slide 24

Slide 24 text

Ωʔʹͳͬͯ͘Δͷ͕ “Swift is a type safe language”

Slide 25

Slide 25 text

ͦͯ͠ ϥΠϒϥϦظ ;ͨͨͼʂʂʂ

Slide 26

Slide 26 text

APIKit + Himotoki

Slide 27

Slide 27 text

APIKit https://github.com/ishkawa/APIKit

Slide 28

Slide 28 text

APIKit Λ API ͷ υΩϡϝϯτͱͯ͠࢖͏

Slide 29

Slide 29 text

https://speakerdeck.com/ishkawa/introducing-apikit

Slide 30

Slide 30 text

GitHub serach ͷ API ͷ৔߹

Slide 31

Slide 31 text

ར༻͢Δํ

Slide 32

Slide 32 text

// https://api.github.com/search/repositories let request = GitHub.Endpoint.SearchRepositories( query: "APIKit", sort: .Stars, order: .Asceding ) GitHub.sendRequest(request) { response in switch response { case .Success(let box): self.repositories = box.unbox case .Fail println(box.value) } }

Slide 33

Slide 33 text

// https://api.github.com/search/repositories let request = GitHub.Endpoint.SearchRepositories( query: "APIKit", sort: .Stars, order: .Asceding ) GitHub.sendRequest(request) { response in switch response { case .Success(let box): self.repositories = box.unbox case .Fail println(box.value) } } DBTF CPYWBMVF 4VDDFTT 3FTQPOTF ϦΫΤετ ੒ޭ 'BJMVSF /4&SSPS ϦΫΤετ ࣦഊ

Slide 34

Slide 34 text

࣮૷

Slide 35

Slide 35 text

class GitHub: API { override class var baseURL: NSURL { return NSURL(string: "https://api.github.com")! } class Endpoint { // https://developer.github.com/v3/search/#search-repositories class SearchRepositories: APIKit.Request { enum Sort: String { case Stars = "stars" case Forks = "forks" case Updated = "updated" } enum Order: String { case Ascending = "asc" case Descending = "desc" } typealias Response = [Repository] ... class func responseFromObject(object: AnyObject) -> Response? { var repositories = [Repository]() if let dictionaries = object["items"] as? [NSDictionary] { for dictionary in dictionaries { if let repository = Repository(dictionary: dictionary) { repositories.append(repository) } } } return repositories } } } }

Slide 36

Slide 36 text

class GitHub: API { override class var baseURL: NSURL { return NSURL(string: "https://api.github.com")! } class Endpoint { // https://developer.github.com/v3/search/#search-repositories class SearchRepositories: APIKit.Request { enum Sort: String { case Stars = "stars" case Forks = "forks" case Updated = "updated" } enum Order: String { case Ascending = "asc" case Descending = "desc" } typealias Response = [Repository] ... class func responseFromObject(object: AnyObject) -> Response? { var repositories = [Repository]() if let dictionaries = object["items"] as? [NSDictionary] { for dictionary in dictionaries { if let repository = Repository(dictionary: dictionary) { repositories.append(repository) } } } return repositories } } } }

Slide 37

Slide 37 text

class Endpoint { class SearchRepositories: APIKit.Request { enum Sort: String { case Stars = "stars" case Forks = "forks" case Updated = "updated" } enum Order: String { case Ascending = "asc" case Descending = "desc" } typealias Response = [Repository] let query: String let sort: Sort let order: Order var URLRequest: NSURLRequest? { return GitHub.URLRequest( method: .GET, path: "/search/repositories", parameters: ["q": query, "sort": sort.rawValue, "order": order.rawValue] ) } init(query: String, sort: Sort = .Stars, order: Order = .Ascending) { self.query = query self.sort = sort self.order = order } func responseFromObject(object: AnyObject) -> Response? { var repositories = [Repository]() if let dictionaries = object["items"] as? [NSDictionary] { for dictionary in dictionaries { if let repository = Repository(dictionary: dictionary) { repositories.append(repository) } } } return repositories } } }

Slide 38

Slide 38 text

class Endpoint { class SearchRepositories: APIKit.Request { enum Sort: String { case Stars = "stars" case Forks = "forks" case Updated = "updated" } enum Order: String { case Ascending = "asc" case Descending = "desc" } typealias Response = [Repository] let query: String let sort: Sort let order: Order var URLRequest: NSURLRequest? { return GitHub.URLRequest( method: .GET, path: "/search/repositories", parameters: ["q": query, "sort": sort.rawValue, "order": order.rawValue] ) } init(query: String, sort: Sort = .Stars, order: Order = .Ascending) { self.query = query self.sort = sort self.order = order } func responseFromObject(object: AnyObject) -> Response? { var repositories = [Repository]() if let dictionaries = object["items"] as? [NSDictionary] { for dictionary in dictionaries { if let repository = Repository(dictionary: dictionary) { repositories.append(repository) } } } return repositories } } } initializer Object Mapping

Slide 39

Slide 39 text

class Endpoint { class SearchRepositories: APIKit.Request { enum Sort: String { case Stars = "stars" case Forks = "forks" case Updated = "updated" } enum Order: String { case Ascending = "asc" case Descending = "desc" } typealias Response = [Repository] let query: String let sort: Sort let order: Order var URLRequest: NSURLRequest? { return GitHub.URLRequest( method: .GET, path: "/search/repositories", parameters: ["q": query, "sort": sort.rawValue, "order": order.rawValue] ) } init(query: String, sort: Sort = .Stars, order: Order = .Ascending) { self.query = query self.sort = sort self.order = order } func responseFromObject(object: AnyObject) -> Response? { var repositories = [Repository]() if let dictionaries = object["items"] as? [NSDictionary] { for dictionary in dictionaries { if let repository = Repository(dictionary: dictionary) { repositories.append(repository) } } } return repositories } } } ΤϯυϙΠϯτͷ৘ใ

Slide 40

Slide 40 text

·ΔͰυΩϡϝϯτΛ ॻ͖͔ࣸͨ͠ͷΑ͏ͳఆٛ

Slide 41

Slide 41 text

ΤϯυϙΠϯτͷఆٛΛ ͜ͷΫϥεʹू໿ԽͰ͖Δ

Slide 42

Slide 42 text

ͨͩ਺͕૿͑ͯ͘Δͱ initializer, object mapper ͕ अຐʹͳͬͯ͘Δ

Slide 43

Slide 43 text

class Endpoint { class SearchRepositories: APIKit.Request { enum Sort: String { case Stars = "stars" case Forks = "forks" case Updated = "updated" } enum Order: String { case Ascending = "asc" case Descending = "desc" } typealias Response = [Repository] let query: String let sort: Sort let order: Order var URLRequest: NSURLRequest? { return GitHub.URLRequest( method: .GET, path: "/search/repositories", parameters: ["q": query, "sort": sort.rawValue, "order": order.rawValue] ) } init(query: String, sort: Sort = .Stars, order: Order = .Ascending) { self.query = query self.sort = sort self.order = order } func responseFromObject(object: AnyObject) -> Response? { var repositories = [Repository]() if let dictionaries = object["items"] as? [NSDictionary] { for dictionary in dictionaries { if let repository = Repository(dictionary: dictionary) { repositories.append(repository) } } } return repositories } } } initializer Object Mapping

Slide 44

Slide 44 text

class Endpoint { class SearchRepositories: APIKit.Request { enum Sort: String { case Stars = "stars" case Forks = "forks" case Updated = "updated" } enum Order: String { case Ascending = "asc" case Descending = "desc" } typealias Response = [Repository] let query: String let sort: Sort let order: Order var URLRequest: NSURLRequest? { return GitHub.URLRequest( method: .GET, path: "/search/repositories", parameters: ["q": query, "sort": sort.rawValue, "order": order.rawValue] ) } init(query: String, sort: Sort = .Stars, order: Order = .Ascending) { self.query = query self.sort = sort self.order = order } class func responseFromObject(object: AnyObject) -> Response? { return object["items"].flatMap(decode) ?? [] } } } Using Himotoki

Slide 45

Slide 45 text

Awesome!!!!

Slide 46

Slide 46 text

Himotoki ͷ͓͔͛Ͱ ΑΓυΩϡϝϯτͱͯ͠ Έ΍͘͢ͳΓ·͢Ͷ

Slide 47

Slide 47 text

Himotoki https://github.com/ikesyo/Himotoki

Slide 48

Slide 48 text

Himotoki Λ API Ϩεϙϯεͷ υΩϡϝϯτͱͯ͠࢖͏

Slide 49

Slide 49 text

http://www.slideshare.net/syoikeda/himotoki-a-typesafe-json-decoding-library

Slide 50

Slide 50 text

Usage struct User: Decodable { let id: Int let login: String let avatar_url: String static func decode(e: Extractor) -> User? { let create = { User($0) } return build( e <| "id", e <| "login", e <| "avatar_url" ).map(create) } }

Slide 51

Slide 51 text

Operators <| // T <|? // T? <|| // [T] <||? // [T]? <|-| // [String: T] <|-|? // [String: T]?

Slide 52

Slide 52 text

tips • SnakeCase → CamelCase • ܕม׵ • ܕม׵ (Enum) • σʔλ֊૚͕ਂ͍৔߹

Slide 53

Slide 53 text

SnakeCase → CamelCase struct User: Decodable { let id: Int let login: String let avatar_url: String static func decode(e: Extractor) -> User? { let create = { User($0) } return build( e <| "id", e <| "login", e <| "avatar_url" ).map(create) } }

Slide 54

Slide 54 text

SnakeCase → CamelCase struct User: Decodable { let id: Int let login: String let avatarURL: String static func decode(e: Extractor) -> User? { let create = { User($0) } return build( e <| "id", e <| "login", e <| "avatar_url" ).map(create) } }

Slide 55

Slide 55 text

ܕ͑͞Ұக͍ͯ͠Ε͹ ໊લ͸ͳΜͰ΋͍͍

Slide 56

Slide 56 text

ܕม׵ struct User: Decodable { let id: Int let login: String let avatarURL: String static func decode(e: Extractor) -> User? { let create = { User($0) } return build( e <| "id", e <| "login", e <| "avatar_url" ).map(create) } }

Slide 57

Slide 57 text

ܕม׵ struct User: Decodable { let id: Int let login: String let avatarURL: NSURL static func decode(e: Extractor) -> User? { let create = { User($0) } return build( e <| "id", e <| "login", (e <| "avatar_url").flatMap { NSURL(string: $0) } ).map(create) } }

Slide 58

Slide 58 text

ܕม׵ (Enum) struct User: Decodable { enum Type: String { case User = "User" case Organization = "Organization" } let id: Int let login: String let type: Type let avatarURL: NSURL static func decode(e: Extractor) -> User? { let create = { User($0) } return build( e <| "id", e <| "login", (e <| "type").flatMap { Type(rawValue: $0) }, e <| "avatar_url").flatMap { NSURL(string: $0) } ).map(create) }

Slide 59

Slide 59 text

σʔλ֊૚͕ਂ͍৔߹ repositories = [ { "id": 3081286, "name": "Tetris", "owner": { "login": "dtrupenn", "id": 872147, "avatar_url": "https://secure.gravatar.com/...", "type": "User" }, ... } ]

Slide 60

Slide 60 text

σʔλ֊૚͕ਂ͍৔߹ struct Repository: Decodable { let id: Int let name: String let owner: User static func decode(e: Extractor) -> Repository? { let create = { Repository($0) } return build( e <| "id", e <| "name", e <| "owner" ).map(create) } } struct User: Decodable { let id: Int let login: String let avatarURL: NSURL static func decode(e: Extractor) -> User? { let create = { User($0) } return build( e <| "id", e <| "login", (e <| "avatar_url").flatMap { NSURL(string: $0) } ).map(create) } }

Slide 61

Slide 61 text

σʔλ֊૚͕ਂ͍৔߹ struct Repository: Decodable { let id: Int let name: String let ownerId: Int static func decode(e: Extractor) -> Repository? { let create = { Repository($0) } return build( e <| "id", e <| “name", e <| [“owner”, “id”], ).map(create) } }

Slide 62

Slide 62 text

ͨ·ʹมͳఆٛΛ͢Δͱ ܕਪ࿦͕ղܾͰ͖ͣʹ ϑΝϯ͕ᄬΓΛ্͛ͨ͋ͱtimeout తͳ ΤϥʔͰίϯύΠϧʹࣦഊ͢Δ

Slide 63

Slide 63 text

ͭ·Γ͸ϑΝϯ͕ᄬΓΛ͋͛͸͡ΊΔͱ ࣗ෼͕มͳఆٛΛ͍ͯ͠ΔՄೳੑ͕ߴ͍ͷͰ ଈࠁݟͳ͓ͨ͠ํ͕͍͍Ͱ͢

Slide 64

Slide 64 text

͓͢͢Ί • Object Mapping ͷఆ͚ٛͩΛ୯ҰϑΝΠϧʹͯ͠ఆٛ͠ ͓͚ͯ͹ݟ΍͘͢ͳΔ • extension Ͱ logic Λఆٛ͢Ε͹σʔλͱৼΔ෣͍͕Θ ͔ΕͯΘ͔Γ΍͍͢ • ctrl + 6 ͨ͠Βͪΐͬͱײಈ͢Δ • ؆୯ͳ΋ͷͰ͍͍͔Βςετ͸ॻ͍ͱ͍ͨํ͕͍͍ • Կ͔ͷมߋͰ decode ʹࣦഊͨ͠ࡍʹݕ஌Ͱ͖ΔͨΊ • stub Λߋ৽͢ΔεΫϦϓτͱ͔͋Ε͹ϝϯς͸͔ͲΔ

Slide 65

Slide 65 text

·ͱΊ APIKit + Himotoki Λ࢖͑͹ݟࣄʹϦϑΝϨ ϯεͬΆ͘ͳΓ·͢Ͷ ͔ͬ͠ΓϨΠϠʔ΋෼͚ΕΔͷͰอक͠΍ ͍͢ίʔυʹͳΓ·͢

Slide 66

Slide 66 text

·ͱΊ શͯΛ͜ΕͰ΍Ε͹͍͍ͱ͍͏Θ͚Ͱͳ͘ ࣗࣾαʔϏεͷAPIͷ৔߹ͳͲϦϑΝϨϯε ·Ͱ༻ҙͯ͠ΔέʔεͳͲ΄΅ͳ͍ͷͰɺ গ͕࣌ؒ͋͘͠ͱ๨͕ͪʹͳͬͯ͠·͏ͷ Ͱͦ͏͍͏࣌ʹศརͰ͢Ͷ ͜͏͍͏Ξϓϩʔν΋͋Γ͔ͳͱࢥ͏࣍ୈ Ͱ͢

Slide 67

Slide 67 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠