OpenAPIを使ってみる(3) iOSアプリ(Swift5)を作成する

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.
  1. Xcodeでプロジェクトを選択する。
  2. TARGETSのプロジェクトを選択する。
  3. 「Build Settings」タブを選択し「always」と入力して検索する。
  4. 「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を無効にする。

  1. Xcodeでプロジェクトを選択する。
  2. TARGETSのプロジェクトを選択する。
  3. 「Info」タブを選択し「Custom iOS Target Properties」に追加する。
  4. 「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()
    }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください