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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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ϑΝΠϧ͔ΒΫϥΠΞϯτίʔυΛग़ྗ͢ΔίϚ
    ϯυϥΠϯ

    View full-size slide

  7. Swagger Editor
    ɹ
    ɹ
    ˰ OPEN SOURCE WEB EDITOR

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  11. 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-

    View full-size slide

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

    View full-size slide

  13. 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'

    View full-size slide

  14. $ 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)

    View full-size slide

  15. # 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'

    View full-size slide

  16. // 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
    }
    }

    View full-size slide

  17. // 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()
    }
    }

    View full-size slide

  18. // 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)
    }

    View full-size slide

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

    View full-size slide

  20. # 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"
    ...
    }
    }

    View full-size slide

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

    View full-size slide

  22. 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/

    View full-size slide

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

    View full-size slide

  24. ςϯϓϨʔτΛϑΥʔΫ͢Δ΄ͲͰ͸ͳ͍มߋ͸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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide