OpenAPIを使って、iOSアプリ(Swift)を作成する。
「OpenAPIを使ってみる(2) Androidアプリ(Kotlin)を作成する」の続き。
- 環境
- macOS Monterey
- openapi-generator-cli 6.0.1(「brew install openapi-generator」でインストール)
前々回に作成したOpenAPIのAPIドキュメントとテストサーバーを使って、iOSアプリを作成する。
openapi_sampleフォルダーの下にiOSアプリを作成する。
openapi_sample/
openapi/
openapi.yml
ios_app/
OpenAPI Generatorでソースコードを生成する
iOS用のソースコード(Swift5)を生成する。
使用できるオプションを確認する。
openapi-generator config-help -g swift5
openapiフォルダーで以下のコマンドを実行し、iOSのソースコードを生成する。
openapi-generator generate -i openapi.yml -g swift5 -o swift5
openapi/swift5フォルダーにソースコードが生成された。
ライブラリのインストール
CocoaPodsを使って、iOSアプリにライブラリを導入できるようにする。
iOSアプリのプロジェクトフォルダーに移動して、Podfileを生成する。
cd ../ios_app
pod init
Podfileファイルを編集する。
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
platform :ios, '14.0' # Xcodeで指定しているバージョンに合わせる
target 'ios_app' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for ios_app
pod 'OpenAPIClient', :path => '../openapi/swift5' # 追加
target 'ios_appTests' do
inherit! :search_paths
# Pods for testing
end
target 'ios_appUITests' do
# Pods for testing
end
end
ライブラリをインストールする。
pod install
以下のメッセージが表示されたので、Xcodeで設定を変更する。
[!] The `ios_appUITests [Debug]` target overrides the `ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES` build setting defined in `Pods/Target Support Files/Pods-ios_app-ios_appUITests/Pods-ios_app-ios_appUITests.debug.xcconfig'. This can lead to problems with the CocoaPods installation
- Use the `$(inherited)` flag, or
- Remove the build settings from the target.
- Xcodeでプロジェクトを選択する。
- TARGETSのプロジェクトを選択する。
- 「Build Settings」タブを選択し「always」と入力して検索する。
- 「Always Embed Swift Standard Libraries」を選択して、Deleteキーで削除する。
もう一度コマンドを実行する。
pod install
正常にインストールできた。
Pod installation complete! There is 1 dependency from the Podfile and 2 total pods installed.
Xcodeのプロジェクトを設定する
「http://〜」にアクセスできるように、info.plistを編集してATSを無効にする。
- Xcodeでプロジェクトを選択する。
- TARGETSのプロジェクトを選択する。
- 「Info」タブを選択し「Custom iOS Target Properties」に追加する。
- 「App Transport Security Settings」→「Allow Arbitrary Loads」の値を「YES」にする。
APIを叩く
注意:Xcodeでプロジェクトを開くときは、XXX.xcodeprojでなく、XXX.xcworkspaceを開く。
main関数で初期設定を行う。
import SwiftUI
import OpenAPIClient
@main
struct swift_appApp: App {
var body: some Scene {
WindowGroup {
ContentView().onAppear{
OpenAPIClientAPI.basePath = "http://192.168.10.109:8080/v1"
}
}
}
}
UsersApiのメソッドを呼ぶことでAPIを叩ける。
// ユーザー一覧を取得する
UsersAPI.listUsers() { (users, error) in
}
// ユーザーを取得する
UsersAPI.getUserById(userId: id) { user, error in
}
サンプルプログラム
「Load Users」ボタンを押すとユーザー一覧を取得し、ユーザーをタップすると詳細を表示する。
import SwiftUI
import OpenAPIClient
class ViewModel: ObservableObject {
@Published var loading:Bool = false
@Published var users:[SimpleUser] = []
@Published var user:User? = nil
func listUsers() {
self.loading = true
self.users = []
DispatchQueue.global(qos: .userInitiated).async {
sleep(1)
UsersAPI.listUsers() { (users, error) in
if let error = error {
print(error.localizedDescription.debugDescription)
self.loading = false
return
}
if let users = users {
print(users)
self.users = users
}
self.loading = false
}
}
}
func getUserById(_ id:Int) {
loading = true
user = nil
DispatchQueue.global(qos: .userInitiated).async {
sleep(1)
UsersAPI.getUserById(userId: id) { user, error in
if let error = error {
print(error.localizedDescription.debugDescription)
self.loading = false
return
}
if let user = user {
self.user = user
}
self.loading = false
}
}
}
func resetUser() {
user = nil
}
}
struct ContentView: View {
@ObservedObject private var viewModel:ViewModel = ViewModel()
var body: some View {
if viewModel.loading {
VStack() {
Text("loading")
}
} else if (viewModel.user != nil) {
List{
Button(
action: {viewModel.resetUser()},
label: {Text("Close")})
Text("id: \(viewModel.user!.id!)")
Text("name: \(viewModel.user!.name!)")
Text("birthday: \(viewModel.user!.birthday!)")
}
} else {
List {
Button(
action: {viewModel.listUsers()},
label: {Text("Load Users")})
ForEach(viewModel.users, id: \.id) { user in
Button(
action: {
let id:Int = user.id!
viewModel.getUserById(id) },
label: {
Text("id: \(user.id!) name: \(user.name!)")
}
)
}
Spacer()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}