Swaggerで始めるAPI定義管理とコードジェネレート

Acbf3391de0494432a92221ffe89f34e?s=47 yohei sugigami
September 15, 2017

 Swaggerで始めるAPI定義管理とコードジェネレート

iOSDC 2017
2017/09/15@早稲田大学理工学部西早稲田キャンパス 63号館

Yohei Suginami ( @susieyy )

Acbf3391de0494432a92221ffe89f34e?s=128

yohei sugigami

September 15, 2017
Tweet

Transcript

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

    @susieyy )
  2. Profile — Yohei Sugigami — susieyy — Twitter / Qiita

    / Github — New App Development Specialization — Clients — Folio.inc — New app developer — Wantedly.inc — Technical advisor
  3. Agenda — ͳͥAPIఆٛΛ໌จԽ͢Δͷ͔ — Swaggerͷ঺հ — ͳͥίʔυδΣωϨʔτΛ͢Δͷ͔ — Swagger Codegenͷ঺հ

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

  5. Swagger — The World's Most Popular Framework for APIs —

    MicrosoftɺGoogleɺIBMΒ͕RSETful APIͷఆٛهड़ͷඪ ४ԽΛ໨ࢦ͢Open API InitiativeΛ্ཱͪ͛ɺπʔϧʹ SwaggerΛ࠾༻ — 2011೥ΑΓϓϩδΣΫτ͕ελʔτ — ఆٛ͸1ͭͷϑΝΠϧʹू໿͞ΕΔ (GitͰߏ੒؅ཧ͕༰қ)
  6. 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ϑΝΠϧ͔ΒΫϥΠΞϯτίʔυΛग़ྗ͢ΔίϚ ϯυϥΠϯ
  7. Swagger Editor ɹ ɹ ˰ OPEN SOURCE WEB EDITOR

  8. None
  9. None
  10. None
  11. None
  12. None
  13. Swagger UI / Mock Server ɹ ɹ Spec ˰ Documents

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

  15. Swagger Codegen Πϯετʔϧ $ brew install swagger-codegen $ swagger-codegen version

    2.2.3 2017೥9݄15೔ݱࡏ v2.2.3 ͕࠷৽
  16. 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-
  17. Swagger Codegen Swi!ͷରԠঢ়گ — Swift4ʹઈࢍରԠத — JSONͷύʔε͸CodableͰ࣮૷ — Optionalͷఆٛʹ΋ରԠ —

    EnumରԠ — API Client෦෼͸Alamofire͕ϕʔε — CocoaPodsͷϥΠϒϥϦͱͯ͠ग़ྗͰ͖Δ — RxSwiftͱ΋࿈ܞՄೳʢ Ϩεϙϯε͕ObservableʹͳΔ ʣ
  18. 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'
  19. $ 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)
  20. None
  21. # 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'
  22. // 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 } }
  23. // 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() } }
  24. // 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<Void> */ open class func deletePetWithRequestBuilder(petId: Int64, apiKey: String? = nil) -> RequestBuilder<Void> { 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<Void>.Type = PetstoreAPI.requestBuilderFactory.getNonDecodableBuilder() return requestBuilder.init(method: "DELETE", URLString: (url?.string ?? URLString), parameters: parameters, isBody: false, headers: headerParameters) }
  25. Q&Aɹ [Q] શϦΫΤετԣஅతʹϦΫΤετ ϔομʔ΍ɺϕʔεύεΛΧελϚΠ ζͰ͖·͔͢ʁ ɹ [A] Ͱ͖·͢ (^o^)

  26. # 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" ... } }
  27. Q&Aɹ [Q] ίʔυδΣωϨʔτͷςϯϓϨʔ τ͸มߋͰ͖·͔͢ʁ ɹ [A] Ͱ͖·͢ (^o^)

  28. 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/
  29. mustacheςϯϓϨʔτͷModel͸͜Μͳײ͡ import Foundation {{#description}} /** {{description}} */{{/description}} {{#isArrayModel}} public typealias

    {{classname}} = [{{arrayModelType}}] {{/isArrayModel}} {{^isArrayModel}} {{#isEnum}} public enum {{classname}}: {{dataType}}, Codable { {{#allowableValues}}{{#enumVars}} case {{name}} =
  30. ςϯϓϨʔτΛϑΥʔΫ͢Δ΄ͲͰ͸ͳ͍มߋ͸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
  31. Q&Aɹ [Q] ̍ͭͷswagger.yamnlϑΝΠϧͰ ؅ཧ͢ΔͱංେԽͯ͠େมͰ͸ͳ͍Ͱ ͔͢ʁ ɹ [A] େมͰ͢ (^_^;)

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

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

  34. Github Flow ( PR, Review, Merge and Tagging )

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

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

    ͳΕ͹ͱئ͍ͬͯΔ