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

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

yohei sugigami
September 15, 2017

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

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

Yohei Suginami ( @susieyy )

yohei sugigami

September 15, 2017
Tweet

More Decks by yohei sugigami

Other Decks in Technology

Transcript

  1. Profile — Yohei Sugigami — susieyy — Twitter / Qiita

    / Github — New App Development Specialization — Clients — Folio.inc — New app developer — Wantedly.inc — Technical advisor
  2. Swagger — The World's Most Popular Framework for APIs —

    MicrosoftɺGoogleɺIBMΒ͕RSETful APIͷఆٛهड़ͷඪ ४ԽΛ໨ࢦ͢Open API InitiativeΛ্ཱͪ͛ɺπʔϧʹ SwaggerΛ࠾༻ — 2011೥ΑΓϓϩδΣΫτ͕ελʔτ — ఆٛ͸1ͭͷϑΝΠϧʹू໿͞ΕΔ (GitͰߏ੒؅ཧ͕༰қ)
  3. 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ϑΝΠϧ͔ΒΫϥΠΞϯτίʔυΛग़ྗ͢ΔίϚ ϯυϥΠϯ
  4. 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-
  5. Swagger Codegen Swi!ͷରԠঢ়گ — Swift4ʹઈࢍରԠத — JSONͷύʔε͸CodableͰ࣮૷ — Optionalͷఆٛʹ΋ରԠ —

    EnumରԠ — API Client෦෼͸Alamofire͕ϕʔε — CocoaPodsͷϥΠϒϥϦͱͯ͠ग़ྗͰ͖Δ — RxSwiftͱ΋࿈ܞՄೳʢ Ϩεϙϯε͕ObservableʹͳΔ ʣ
  6. 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'
  7. $ 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)
  8. # 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'
  9. // 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 } }
  10. // 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() } }
  11. // 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) }
  12. # 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" ... } }
  13. 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/
  14. mustacheςϯϓϨʔτͷModel͸͜Μͳײ͡ import Foundation {{#description}} /** {{description}} */{{/description}} {{#isArrayModel}} public typealias

    {{classname}} = [{{arrayModelType}}] {{/isArrayModel}} {{^isArrayModel}} {{#isEnum}} public enum {{classname}}: {{dataType}}, Codable { {{#allowableValues}}{{#enumVars}} case {{name}} =
  15. ςϯϓϨʔτΛϑΥʔΫ͢Δ΄ͲͰ͸ͳ͍มߋ͸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