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

ドキュメントみたいな API Client を求めて

ドキュメントみたいな API Client を求めて

横浜へなちょこiOS #20

Toshihiro Morimoto

June 20, 2015
Tweet

More Decks by Toshihiro Morimoto

Other Decks in Technology

Transcript

  1. API Client ࢙ 1. UIKitظ (NSURLConnection, NSURLSession ౳) 2. ϥΠϒϥϦظ

    (AFNetwork, Alamofire ౳) 3. UIKit + αظ (؆୯ͳ Wrapper Λࣗ෼Ͱॻ͘)
  2. // 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) } }
  3. // 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 ϦΫΤετ ࣦഊ
  4. 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 } } } }
  5. 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 } } } }
  6. 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 } } }
  7. 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
  8. 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 } } } ΤϯυϙΠϯτͷ৘ใ
  9. 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
  10. 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
  11. 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) } }
  12. Operators <| // T <|? // T? <|| // [T]

    <||? // [T]? <|-| // [String: T] <|-|? // [String: T]?
  13. 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) } }
  14. 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) } }
  15. ܕม׵ 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) } }
  16. ܕม׵ 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) } }
  17. ܕม׵ (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) }
  18. σʔλ֊૚͕ਂ͍৔߹ repositories = [ { "id": 3081286, "name": "Tetris", "owner":

    { "login": "dtrupenn", "id": 872147, "avatar_url": "https://secure.gravatar.com/...", "type": "User" }, ... } ]
  19. σʔλ֊૚͕ਂ͍৔߹ 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) } }
  20. σʔλ֊૚͕ਂ͍৔߹ 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) } }
  21. ͓͢͢Ί • Object Mapping ͷఆ͚ٛͩΛ୯ҰϑΝΠϧʹͯ͠ఆٛ͠ ͓͚ͯ͹ݟ΍͘͢ͳΔ • extension Ͱ logic

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