SPMとSwift OpenAPIGenerator

847 Views

February 24, 24

スライド概要

沖縄モバイルアプリ開発勉強会 #2(https://okimobdev.connpass.com/event/306586/)で登壇した資料です。

profile-image

noteという会社でiOSアプリの開発をしています。 個人アプリも色々あります。 # Type: https://type-markdown.app WebCollector: https://webcollector.app/ Pity: https://freetimepicker.firebaseapp.com

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

SPMとSwift OpenAPIGenerator @fromkk かっくん

2.

自己紹介 • 名前:植岡 和哉(かっくん) • SNS: @fromkk (X, note, GitHub, Qiita, Zenn, etc…) • フリーランスのiOSデベロッパー • 埼玉県所沢市に中古マンションを購入・リノベーションして住 んでいます • 猫が2匹います • 主な趣味は写真です。最近編み物始めました。

3.

kyuとは https://kyu-o.com/ • TranSe Inc.が運営しているイメージングブランド • 過去にはバックパック、クロス、SDカードスリーブを生産・販売 • 今はカメラ・アプリの開発に注力中

4.

kyu app https://apps.apple.com/jp/app/kyu-memories-never-fade/id6451095022 • 現在(2024/02/24)招待された方のみ公開中 • 簡単にいうと部屋を作って友だちを招待して動画を撮影して共有するアプリ • 動画は1動画9秒制限、1日に9つまでアップロード可能 • 動画は一度下書きに保存され、6時間に一度公開される • Highlight機能でまとめ動画を生成する • Highlight動画にはSpotifyからBGMを設定することができる

5.

kyu app https://apps.apple.com/jp/app/kyu-memories-never-fade/id6451095022 • 現在(2024/02/24)招待された方のみ公開中 • 簡単にいうと部屋を作って友だちを招待して動画を撮影して共有するアプリ • 動画は1動画9秒制限、1日に9つまでアップロード可能 • 動画は一度下書きに保存され、6時間に一度公開される • Highlight機能でまとめ動画を生成する • Highlight動画にはSpotifyからBGMを設定することができる

6.

機能紹介

7.

OpenAPI Generator • OpenAPI(元Swagger)はymlファイルなどによりAPIのリクエスト・レスポンスの パラメータや型を定義する仕組み • AppleがSwiftでライブラリを提供 • サーバーサイドやクライアントのコードも生成してくれる • DerivedData配下に設置されるので実態は見えない • 詳しくはWWDC 2023の動画や公式ドキュメントなどを見てください • 年末に1.0.0がリリースされた🎉 • その後もアップデート繰り返し現在(2024/02/17)は1.2.1

8.

参考 • https://developer.apple.com/videos/play/wwdc2023/10171/ • https://zenn.dev/kamimi01/articles/7c6f79e42c3750 • https://zenn.dev/kamimi01/articles/1bb2a5f6f2bcf0

9.

今回気になったこと • WWDCの発表の中ではメインアプリの中にSPM経由でライブラリを追加して いた • SPMを利用したマルチモジュール構成の場合にどうなるのか気になった • 結論から言うと、特にメインアプリに追加するのと大きく変わらずに実装す ることができた • 設定ファイルとAPIの定義ファイルをPackage内に入れておくだけ

10.

注意点 • 認証情報などの管理までうまくはやってくれない • 認証情報やカスタムヘッダーなどはClientMiddlewareを作成して注入する • 今回はアクセストークンを取得するAPIを手動で叩いて、その結果を元に検 索APIをコールするサンプルを作成してみた • 事前にClientIDとClientSecretを取得しておく • https://developer.spotify.com/ に登録して、アプリを登録することで取 得できる

11.

Spotifyの検索APIを叩いてみる

12.

実装 必要なパッケージの追加 dependencies: [ .package(url: .package(url: .package(url: .package(url: ], "https://github.com/apple/swift-openapi-generator", from: "1.2.1"), "https://github.com/apple/swift-openapi-runtime", from: "1.3.2"), "https://github.com/apple/swift-openapi-urlsession", from: "1.0.1"), "https://github.com/apple/swift-http-types", from: "1.0.3"),

13.

実装 ターゲットに必要なライブラリやファイルを追加 targets: [ .target(name: "SpotifyAPI", dependencies: [ .product(name: "OpenAPIURLSession", package: "swift-openapi-urlsession"), .product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"), .product(name: "HTTPTypes", package: "swift-http-types"), ], resources: [ .process("openapi-generator-config.yaml"), .process("openapi.yaml"), ], plugins: [ .plugin(name: "OpenAPIGenerator", package: "swift-openapi-generator") ]) ] 設定ファイルと定義ファイルを忘れずに

14.

実装 openapi-generator-con g.yaml generate: - types - client fi 今回はクライアントだが、サーバーのコード生成もできる

15.
[beta]
実装
openapi.yaml
openapi: '3.0.3'
info:
description: |
You can use Spotify's Web API to discover music and podcasts, manage your Spotify library, control audio playback, and much more. Browse our
available Web API endpoints using the sidebar at left, or via the navigation bar on top of this page on smaller screens.
In order to make successful Web API requests your app will need a valid access token. One can be obtained through <a href="https://
developer.spotify.com/documentation/general/guides/authorization-guide/">OAuth 2.0</a>.
The base URI for all Web API requests is `https://api.spotify.com/v1`.
Need help? See our <a href="https://developer.spotify.com/documentation/web-api/guides/">Web API guides</a> for more information, or visit the
<a href="https://community.spotify.com/t5/Spotify-for-Developers/bd-p/Spotify_Developer">Spotify for Developers community forum</a> to ask
questions and connect with other developers.
version: '1.0.0'
title: 'Spotify Web API'
termsOfService: 'https://developer.spotify.com/terms/'
contact:
name: Spotify for Developers Community
url: https://community.spotify.com/t5/Spotify-for-Developers/bd-p/Spotify_Developer
servers:
- url: https://api.spotify.com/v1
tags:
- name: Albums
- name: Artists
.
.
.
https://developer.spotify.com/reference/web-api/open-api-schema.yaml からダウンロード

16.
[beta]
実装
アクセストークンの取得
func token() async throws -> SpotifyAPIAccessToken {
let url = URL(string: "https://accounts.spotify.com/api/token")!
var request = URLRequest(url: url)
guard let clientId = SpotifyAuth.clientId else {
throw SpotifyAPIClientError.noClientId
}
guard let clientSecret = SpotifyAuth.clientSecret else {
throw SpotifyAPIClientError.noClientSecret
}
if let data = "\(clientId):\(clientSecret)".data(using: .utf8) {
request.allHTTPHeaderFields = [
"Authorization": "Basic \(data.base64EncodedString())",
"Content-Type": "application/x-www-form-urlencoded",
]
}
request.httpBody = "grant_type=client_credentials".data(using: .utf8)
request.httpMethod = "POST"
let configuration = URLSessionConfiguration.default
let urlSession = URLSession(configuration: configuration)
let (data, _) = try await urlSession.data(for: request)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(SpotifyAPIAccessToken.self, from: data)
return result
}

17.
[beta]
実装
APIのコール
public func search(_ q: String) async throws -> [SpotifyTrack] {
let accessToken = try await token()
let accessTokenMiddleware = AccessTokenMiddleware(accessToken: accessToken.accessToken)
let client = Client(
serverURL: try Servers.server1(),
transport: URLSessionTransport(),
middlewares: [accessTokenMiddleware]
)
let headers = Operations.search.Input.Headers()
let response = try await client.search(
query: Operations.search.Input.Query(q: q, _type: [.track]),
headers: headers
)
switch response {
case let .ok(items):
guard let tracks = try items.body.json.tracks?.value2.items else {
return []
}
let results = tracks.map {
SpotifyTrack(
id: $0.id,
title: $0.name,
artistName: $0.artists?.first?.name,
uri: $0.uri,
artworkUrl: $0.album?.value1.value1.images.first.flatMap {
URL(string: $0.url)
},
previewUrl: $0.preview_url.flatMap { URL(string: $0) },
href: $0.external_urls?.value1.spotify.flatMap { URL(string: $0) }
)
}
return results
default:
throw SpotifyAPIClientError.failed
}
}

18.

結果

19.

まとめ • Swift OpenAPI GeneratorをSwift Package Managerでどのように利用す るのか試してみました • 結果的にメインアプリで実装するのと同様に実装することができました • GitHubにサンプルコードをアップしています • https://github.com/fromkk/SpotifyAPISampler