Slide 1

Slide 1 text

SwaggerͰ࢝ΊΔAPIఆٛ؅ཧͱ ίʔυδΣωϨʔτ ɹ iOSDC 2017 2017/09/15@ૣҴాେֶཧ޻ֶ෦੢ૣҴాΩϟϯύε 63߸ؗ Yohei Suginami ( @susieyy )

Slide 2

Slide 2 text

Profile — Yohei Sugigami — susieyy — Twitter / Qiita / Github — New App Development Specialization — Clients — Folio.inc — New app developer — Wantedly.inc — Technical advisor

Slide 3

Slide 3 text

Agenda — ͳͥAPIఆٛΛ໌จԽ͢Δͷ͔ — Swaggerͷ঺հ — ͳͥίʔυδΣωϨʔτΛ͢Δͷ͔ — Swagger Codegenͷ঺հ — δΣωϨʔτ͞ΕͨSwiftίʔυ — ·ͱΊ

Slide 4

Slide 4 text

ͳͥAPIఆٛΛ໌จԽ͢Δͷ͔ — ΫϥΠΞϯτͱαʔόͷΤϯδχΞ૒ํ͕ᴥᴪͳ͘ڞ௨ೝ ࣝΛ߹ΘͤΒΕɺίϥϘϨʔγϣϯ͕ԁ׈ʹͳΔ — ஋ͷܕ΍ɺnullͷ༗ແ΋໌ࣔԽͰ͖Δ(҉໧ͷϧʔϧͷഉআ) — APIఆٛΛ͔ͬ͠Γ࣮ࢪ্ͨ͠Ͱɺ࣮૷ʹऔΓֻ͔Δͱख໭ Γ͕গͳ͍

Slide 5

Slide 5 text

Swagger — The World's Most Popular Framework for APIs — MicrosoftɺGoogleɺIBMΒ͕RSETful APIͷఆٛهड़ͷඪ ४ԽΛ໨ࢦ͢Open API InitiativeΛ্ཱͪ͛ɺπʔϧʹ SwaggerΛ࠾༻ — 2011೥ΑΓϓϩδΣΫτ͕ελʔτ — ఆٛ͸1ͭͷϑΝΠϧʹू໿͞ΕΔ (GitͰߏ੒؅ཧ͕༰қ)

Slide 6

Slide 6 text

Swagger terminology Key Explanation Swagger RSETful APIΛఆٛ͢Δ࢓༷ͱͦΕʹؔ࿈͢Δπʔϧ܈ ͷ૯শ Swagger Spec Swagger࢓༷ʹ४ͨ͡ɺRESTful APIΠϯλʔϑΣΠε Λهड़͢ΔͨΊͷϑΥʔϚοτ(YAML/JSON)ɺϞσϧ ఆٛ͸JSON Schemaͷαϒηοτ Swagger Editor SpecϑΝΠϧΛฤू͢ΔΤσΟλʔɺϦΞϧλΠϜߏ จνΣοΫɺఆٛՄࢹԽ Swagger UI SpecϑΝΠϧ͔ΒAPIυΩϡϝϯτΛੜ੒͢Δπʔϧ Swagger Mock Server SpecϑΝΠϧ͔ΒϞοΫαʔόΛੜ੒ Swagger Codegen SpecϑΝΠϧ͔ΒΫϥΠΞϯτίʔυΛग़ྗ͢ΔίϚ ϯυϥΠϯ

Slide 7

Slide 7 text

Swagger Editor ɹ ɹ ˰ OPEN SOURCE WEB EDITOR

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Swagger UI / Mock Server ɹ ɹ Spec ˰ Documents & Mock API Server

Slide 14

Slide 14 text

ͳͥίʔυδΣωϨʔτΛ͢Δͷ͔ ઃܭͱίʔυͷဃ཭๷ࢭ͍ͨ͠ — ௨৴࣌ʢૄ௨࣌ʣʹαʔόͱΫϥΠΞϯτؒͷ࣮૷ᴥᴪΑ Δ՝୊ൃੜΛ཈ࢭ͍ͨ͠ — ࣮૷޻਺։ൃͷεϐʔυͷ޲্ — ਓҝతͳϛεͷ཈ࢭʹΑΔ඼࣭ͷ޲্

Slide 15

Slide 15 text

Swagger Codegen Πϯετʔϧ $ brew install swagger-codegen $ swagger-codegen version 2.2.3 2017೥9݄15೔ݱࡏ v2.2.3 ͕࠷৽

Slide 16

Slide 16 text

Swagger Codegen Swift4, KotlinͳͲ৽͍͠ݴޠʹ΋ੵۃతʹରԠʂ $ swagger-codegen Available languages: swi!4, swi!3, swi!, kotlin, Java akka-scala, android, apache2, apex, aspnet5, aspnetcore, async-scala, bash, csharp, clojure, cwiki, cpprest, CsharpDotNet2, dart, elixir, eiffel, erlang-server, finch, flash, python-flask, go, go-server, groovy, haskell, jmeter, jaxrs-cxf-client, jaxrs-cxf, java, inflector, jaxrs-cxf-cdi, jaxrs-spec, jaxrs, msf4j, java-play-framework, jaxrs-

Slide 17

Slide 17 text

Swagger Codegen Swi!ͷରԠঢ়گ — Swift4ʹઈࢍରԠத — JSONͷύʔε͸CodableͰ࣮૷ — Optionalͷఆٛʹ΋ରԠ — EnumରԠ — API Client෦෼͸Alamofire͕ϕʔε — CocoaPodsͷϥΠϒϥϦͱͯ͠ग़ྗͰ͖Δ — RxSwiftͱ΋࿈ܞՄೳʢ Ϩεϙϯε͕ObservableʹͳΔ ʣ

Slide 18

Slide 18 text

Swagger Codegen ࣮ߦํ๏ #!/bin/sh # PodͷόʔδϣϯΛΠϯΫϦϝϯτͰ͖Δͱศར now_version=`cat version-swift` next_version=`semver bump patch $now_version` # unwrapRequired=trueʹ͢ΔͱRequiredͰఆٛͨ͠ϓϩύςΟ͸NonOptionalʹͳΔ URL=https://github.com/susieyy/petstore-api-clinet-swift4 swagger-codegen generate \ -i swagger.yaml \ -l swift4 \ -o ./iOS-API-CLIENT \ --additional-properties projectName='Petstore' \ --additional-properties unwrapRequired=true \ --additional-properties podVersion="$next_version" \ --additional-properties podHomepage="$URL" \ --additional-properties podSummary="PetstoreAPIClinet" \ --additional-properties podSource="{ :git => '$URL'}.merge({ :tag => '$next_version' })" \ --additional-properties responseAs='RxSwift'

Slide 19

Slide 19 text

$ swagger-codegen config-help -l swift4 CONFIG OPTIONS sortParamsByRequiredFlag Sort method arguments to place required parameters before optional parameters. (Default: true) ensureUniqueParams Whether to ensure parameter names are unique in an operation (rename parameters that are not). (Default: true) allowUnicodeIdentifiers boolean, toggles whether unicode identifiers are allowed in names or not, default is false (Default: false) projectName Project name in Xcode responseAs Optionally use libraries to manage response. Currently PromiseKit, RxSwift are available. unwrapRequired Treat 'required' properties in response as non-optional (which would crash the app if api returns null as opposed to required option specified in json schema podSource Source information used for Podspec podVersion Version used for Podspec podAuthors Authors used for Podspec podSocialMediaURL Social Media URL used for Podspec podDocsetURL Docset URL used for Podspec podLicense License used for Podspec podHomepage Homepage used for Podspec podSummary Summary used for Podspec podDescription Description used for Podspec podScreenshots Screenshots used for Podspec podDocumentationURL Documentation URL used for Podspec swiftUseApiNamespace Flag to make all the API classes inner-class of {{projectName}}API hideGenerationTimestamp hides the timestamp when files were generated (Default: true)

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

# Petstore.podspec Pod::Spec.new do |s| s.name = 'Petstore' s.ios.deployment_target = '9.0' s.osx.deployment_target = '10.11' s.version = '0.0.1' s.source = { :git => 'https://github.com/susieyy/petstore-api-clinet-swift4' }.merge({ :tag => '0.0.1' }) s.authors = 'Swagger Codegen' s.license = 'Proprietary' s.homepage = 'https://github.com/susieyy/petstore-api-clinet-swift4' s.summary = 'PetstoreAPIClinet' s.source_files = 'Petstore/Classes/Swaggers/**/*.swift' s.dependency 'RxSwift', '~> 3.4.1' s.dependency 'Alamofire', '~> 4.5' end # Podfile pod 'PetstoreAPIClinet', \ git: => 'https://github.com/susieyy/petstore-api-clinet-swift4', \ tag: => 'v0.0.1'

Slide 22

Slide 22 text

// Model import Foundation open class Pet: Codable { public enum Status: String, Codable { case available = "available" case pending = "pending" case sold = "sold" } public var id: Int64? public var category: Category? public var name: String public var photoUrls: [String] public var tags: [Tag]? /** pet status in the store */ public var status: Status? public init(id: Int64?=nil, category: Category?=nil, name: String, photoUrls: [String], tags: [Tag]?=nil, status: Status?=nil) { self.id = id self.category = category self.name = name self.photoUrls = photoUrls self.tags = tags self.status = status } }

Slide 23

Slide 23 text

// API Client open class PetAPI { /** Finds Pets by status - parameter status: (query) Status values that need to be considered for filter - parameter completion: completion handler to receive the data and the error objects */ open class func findPetsByStatus(status: [String], completion: @escaping ((_ data: [Pet]?,_ error: Error?) -> Void)) { findPetsByStatusWithRequestBuilder(status: status).execute { (response, error) -> Void in completion(response?.body, error); } } /** Finds Pets by status - parameter status: (query) Status values that need to be considered for filter - returns: Observable<[Pet]> */ open class func findPetsByStatus(status: [String]) -> Observable<[Pet]> { // responseAs='RxSwift'ͷ৔߹ return Observable.create { observer -> Disposable in findPetsByStatus(status: status) { data, error in if let error = error { observer.on(.error(error as Error)) } else { observer.on(.next(data!)) } observer.on(.completed) } return Disposables.create() } }

Slide 24

Slide 24 text

// API Client open class PetAPI { /** Deletes a pet - DELETE /pet/{petId} - - OAuth: - type: oauth2 - name: petstore_auth - parameter petId: (path) Pet id to delete - parameter apiKey: (header) (optional) - returns: RequestBuilder */ open class func deletePetWithRequestBuilder(petId: Int64, apiKey: String? = nil) -> RequestBuilder { var path = "/pet/{petId}" path = path.replacingOccurrences(of: "{petId}", with: "\(petId)", options: .literal, range: nil) let URLString = PetstoreAPI.basePath + path let parameters: [String:Any]? = nil let url = NSURLComponents(string: URLString) let nillableHeaders: [String: Any?] = [ "api_key": apiKey ] let headerParameters = APIHelper.rejectNilHeaders(nillableHeaders) let requestBuilder: RequestBuilder.Type = PetstoreAPI.requestBuilderFactory.getNonDecodableBuilder() return requestBuilder.init(method: "DELETE", URLString: (url?.string ?? URLString), parameters: parameters, isBody: false, headers: headerParameters) }

Slide 25

Slide 25 text

Q&Aɹ [Q] શϦΫΤετԣஅతʹϦΫΤετ ϔομʔ΍ɺϕʔεύεΛΧελϚΠ ζͰ͖·͔͢ʁ ɹ [A] Ͱ͖·͢ (^o^)

Slide 26

Slide 26 text

# APIs.swift open class PetstoreAPI { open static var basePath = "http://petstore.swagger.io/v2" open static var customHeaders: [String:String] = [:] ... } # Your code / eg. @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions: ...) { PetstoreAPI.basePath = "http://susieyy.com/debug" PetstoreAPI.customHeaders["APP_VERSION"] = "1.0.0" PetstoreAPI.customHeaders["APP_LANG"] = "ja" ... } }

Slide 27

Slide 27 text

Q&Aɹ [Q] ίʔυδΣωϨʔτͷςϯϓϨʔ τ͸มߋͰ͖·͔͢ʁ ɹ [A] Ͱ͖·͢ (^o^)

Slide 28

Slide 28 text

mustacheg1ݴޠͰςϯϓϨʔτ͕هड़͞Ε͍ͯΔ2 δΣωϨʔτίϚϯυʹύϥϝʔλʔΛ௥Ճ $ swagger-codegen generate \ -i swagger.yaml \ -l swift4 \ -t my-custom-template-dir \ # ! ௥Ճ -o ./iOS-API-CLIENT 2 https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen/ src/main/resources/swift4/model.mustache 1 https://mustache.github.io/

Slide 29

Slide 29 text

mustacheςϯϓϨʔτͷModel͸͜Μͳײ͡ import Foundation {{#description}} /** {{description}} */{{/description}} {{#isArrayModel}} public typealias {{classname}} = [{{arrayModelType}}] {{/isArrayModel}} {{^isArrayModel}} {{#isEnum}} public enum {{classname}}: {{dataType}}, Codable { {{#allowableValues}}{{#enumVars}} case {{name}} =

Slide 30

Slide 30 text

ςϯϓϨʔτΛϑΥʔΫ͢Δ΄ͲͰ͸ͳ͍มߋ͸Patch͕ศར $ patch --verbose --no-backup-if-mismatch -p1 < diff-swift-podspec.patch diff --git a/iOS-API-CLIENT/API.podspec b/iOS-API-CLIENT/API.podspec index d713686..05ae4b3 100644 --- a/iOS-API-CLIENT/API.podspec +++ b/iOS-API-CLIENT/API.podspec @@ -8,6 +8,6 @@ Pod::Spec.new do |s| s.license = 'Proprietary' s.homepage = 'https://github.com/susieyy/Mobile-API-Swagger' s.summary = 'SwaggerClientAPI' - s.source_files = 'API/Classes/Swaggers/**/*.swift' + s.source_files = 'iOS-API-CLIENT/API/Classes/Swaggers/**/*.swift' s.dependency 'Alamofire', '~> 4.5' end

Slide 31

Slide 31 text

Q&Aɹ [Q] ̍ͭͷswagger.yamnlϑΝΠϧͰ ؅ཧ͢ΔͱංେԽͯ͠େมͰ͸ͳ͍Ͱ ͔͢ʁ ɹ [A] େมͰ͢ (^_^;)

Slide 32

Slide 32 text

Multi-file SwaggerͰఆٛ(swagger.yaml)෼ׂ

Slide 33

Slide 33 text

Q&Aɹ [Q] ͲͷΑ͏ʹఆٛͱίʔυδΣω ϨʔτΛӡ༻͍ͯ͠·͔͢ʁ ɹ [A] Gitͷswagger.yamlͷλάͱ PodspecόʔδϣϯͷҰக͕େࣄͰ͢

Slide 34

Slide 34 text

Github Flow ( PR, Review, Merge and Tagging )

Slide 35

Slide 35 text

Gitͷswagger.yamlͷλάͱPodspecόʔδϣϯΛҰகͤ͞ Δ͜ͱͰ — δΣωϨʔτ͞ΕͨSwiftίʔυʢCocoaPods)͕ɺͲͷAPI ఆٛʢswagger.yamlʣΛݩʹੜ੒͞Ε͔ͨGitͷλάΛ௥͏ ͜ͱͰಛఆͰ͖Δ — ΞϓϦͷϦΫΤετϔομʔʹPodspecόʔδϣϯΛ௥Ճ͠ ͓ͯ͘ͱɺτϥϒϧγϡʔςΟϯά͠΍͍͢

Slide 36

Slide 36 text

·ͱΊ — iPhoneɺiOS͕ϓϩμΫτͱͯ͠੒ख़͢ΔதɺSwiftɺiOS ։ൃͷ࡞Γํ΋ϊ΢ϋ΢͕ίϛϡχςΟʹཷ·Γɺ੒ख़͕࢝ ·͖͍ͬͯͯΔ — iOS։ൃͷࠓޙ͸ɺ։ൃޮ཰Խ΍඼࣭޲্ͷ؍఺ΛϑΥʔΧ ε͢Δέʔε͕૿͑ͯ͘ΔͷͰ͸ — SwaggerͰͷAPIͷఆٛٴͼɺίʔυδΣωϨʔτ͕Ұॿʹ ͳΕ͹ͱئ͍ͬͯΔ