【初心者向け】iPad だけで Google の AI モデル「Gemini」を使ったアプリを作ってみよう！

_この記事は [SwiftWednesday Advent Calendar 2023](https://qiita.com/advent-calendar/2023/swift-wednesday) の15日目の記事です。_

https://qiita.com/advent-calendar/2023/swift-wednesday

-----

Gemini など、Google の生成系 AI モデルを使ったアプリを、iPad だけで作ってみましょう！

![Screenshot 2023-12-15 at 2.46.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/4136aea6-5e91-d62a-c444-cf9ba24a47d9.png)


# 環境
ここでは iPadOS 17.2 以降を搭載し、Swift Playgrounds 4.4.1 以降が使える iPad を用いてアプリを作ります。うまく動かない場合は iPadOS のアップデートや Swift Playgrounds に対応しているか確認してください。

:::note info
[Mac のための「Swift Playgrounds」](https://apps.apple.com/jp/app/id1496833156)でも同じ手順でアプリを作ることができますが、この記事のスクリーンショットはすべて iPad のものです。
:::

# 「Swift Playgrounds」をダウンロードする
iPad でアプリを作るために、「Swift Playgrounds」をダウンロードします。

https://apps.apple.com/jp/app/id908519492

[![Screenshot 2023-12-15 at 0.50.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/71d9e052-76d5-733b-492e-a1b01e91706f.png)](https://apps.apple.com/jp/app/id908519492)

# 「App」を新規作成し、「Google AI SDK for Swift」を追加する
「Swift Playgrounds」を開いて、画面左下の「App」を選び、新規作成された「マイ App」を開きます。

:::note info
すでに「マイ App」という名前のプレイグラウンドプロジェクトがある場合は、異なる名前になります。アプリのアイコンは以下のスクリーンショットと異なる場合があります。
:::

![Screenshot 2023-12-15 at 1.01.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/43643cab-d9c4-8648-b54d-df9ceed066b0.png)

左のメニューからファイルの作成メニューを開き、「Swiftパッケージ」を選びます。

![Screenshot 2023-12-15 at 1.05.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/95c9eab6-0807-2e04-1e1b-0aa378e0ccbb.png)

「パッケージURL」に以下の URL を入力（コピー&ペースト）して、確定します。

```
https://github.com/google/generative-ai-swift
```

![Screenshot 2023-12-15 at 1.06.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/e27b6c59-6d9c-2861-ed1e-1bdad848b84b.png)

「バージョン」は、今回は 0.4.3 を使います。また「追加する製品」の「GoogleGenerativeAI」をオンにします。右上の「Appプレイグラウンドに追加」を選びます。

![Screenshot 2023-12-15 at 10.36.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/c13740aa-ba30-ab5a-f1ca-18cd4fcd6348.png)

# AI と会話するためのアプリの画面を作る
AI とテキストメッセージで会話するために必要な UI 要素を、SwiftUI で作ります。
はじめから用意されている `ContentView` という Swift ファイルの内容を、以下のように書き換えました。

:::note info
このコードは最低限のプログラムです。よりクオリティの高いものを目指す場合は、さまざまな工夫の余地があります。
:::

![Screenshot 2023-12-15 at 2.47.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/4adb50a7-8381-f2f3-f820-6fc019d54c67.png)

```swift:スクリーンショット内と同じコード
import SwiftUI

struct ContentView: View {
    @State private var prompt = ""
    @State private var message = "AI からのメッセージ"
    
    var body: some View {
        VStack {
            Label("自分", systemImage: "person.fill")
            TextField("自分", text: $prompt, prompt: Text("メッセージを入力"))
                .frame(maxWidth: .infinity, alignment: .leading)
            Divider()
            Label("AI", systemImage: "globe")
            Text(message)
                .frame(maxWidth: .infinity, alignment: .leading)
        }
        .padding()
    }
}
```

# AI と会話するためのアプリの通信部分を作る
AI とテキストメッセージで会話するために必要な通信の部分を作ります。
左のメニューからファイルの作成メニューを開き、「Swiftファイル」を選びます。

![Screenshot 2023-12-15 at 2.48.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/1efd6891-1d06-5f16-b5d3-c5c7b1ed829c.png)

今回は「新規ファイル」の名前を `ContentObject` にしました。

![Screenshot 2023-12-15 at 1.33.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/18604260-2400-0804-00d8-306a48b9b10e.png)

`ContentObject` Swift ファイルの内容を以下のように書き換えました。

:::note info
これらのコードも最低限のプログラムです。よりクオリティの高いものを目指す場合は、さまざまな工夫の余地があります。
:::

![Screenshot 2023-12-15 at 2.20.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/31b933fe-872e-6759-a8b9-6b9d8e6f0c61.png)

```swift:スクリーンショット内と同じコード
import Foundation
import GoogleGenerativeAI

@MainActor @Observable
final class ContentObject {
    private(set) var isBusy = false
    private(set) var message = "AI からの返信"
    
    private let apiKey = "APIキーを入力"
    private let model: GenerativeModel
    private let chat: Chat
    private var task: Task<(), Never>?
    
    init() {
        model = GenerativeModel(name: "gemini-pro", apiKey: apiKey)
        chat = model.startChat()
    }
    
    func send(prompt: String) async {
        task?.cancel()
        
        task = Task {
            defer { isBusy = false }
            isBusy = true
            message = ""
            
            do {
                let response = chat.sendMessageStream(prompt)
                for try await chunk in response {
                    if let text = chunk.text {
                        message += text
                    }
                }
            } catch {
                message = error.localizedDescription
            }
        }
    }
}
```

# AI と会話するためのアプリの画面と通信部分をつなげる
アプリの通信部分である `ContentObject` と、アプリの画面部分である `ContentView` とをつなぎます。
`ContentView` という Swift ファイルの内容を、さらに以下のように書き換えました。

![Screenshot 2023-12-15 at 2.50.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/3fa42eed-a077-96ee-6cbc-09351add1040.png)

```diff_swift:スクリーンショット内と同じコード
import SwiftUI

struct ContentView: View {
+   private let object: ContentObject
    @State private var prompt = ""
-   @State private var message = "AI からのメッセージ"

+   @MainActor
+   init() {
+       object = ContentObject()
+   }
    
    var body: some View {
        VStack {
            Label("自分", systemImage: "person.fill")
            TextField("自分", text: $prompt, prompt: Text("メッセージを入力"))
                .frame(maxWidth: .infinity, alignment: .leading)
            Divider()
            Label("AI", systemImage: "globe")
-           Text(message)
+           Text(object.isBusy ? "考え中" : object.message)
                .frame(maxWidth: .infinity, alignment: .leading)
        }
        .padding()
+       .onSubmit {
+           Task { await object.send(prompt: prompt) }
+       }
    }
}
```

# Google AI SDK で使用する API キーを取得してセットする
Google AI Studio の https://makersuite.google.com/app/apikey にアクセスします。

![Screenshot 2023-12-15 at 2.31.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/1e54b7f8-e7bc-0a3e-e02e-ca7f9ef8ac37.png)

「Get API Key」のページにある、「Create API key in new project」を選びます。しばらくすると「API key generated」と API キーが表示されるので、「Copy」します。

:::note alert
「API key generated」の説明にあるように、API キーを共有したり、自分以外の人が閲覧できるコードに埋め込んだりしないでください。
:::

![Screenshot 2023-12-15 at 2.34.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/6af5fe27-c45e-172a-9d76-8cbac0d14acb.png)

`ContentObject` Swift ファイルを開き、`APIキーを入力` としていた部分にコピーしてきた API キーをペーストします。

![Screenshot 2023-12-15 at 2.38.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/aeb5a7c1-641c-ebc5-d520-7e1fd9f2f8d1.png)

```diff_swift:API キーが "AlzaSyC1234567890AbCdEfGhljKIMnOpQrStUv" の例
-   private let apiKey = "APIキーを入力"
+   private let apiKey = "AlzaSyC1234567890AbCdEfGhljKIMnOpQrStUv"
```

# AI と会話してみる！
ここまでできると完成です！画面上の▶️を選んで、自分が書いたコードを実行してみましょう。メッセージを入力して Submit すると、AI からお返事がきます。

![Screenshot 2023-12-15 at 2.46.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90125/4136aea6-5e91-d62a-c444-cf9ba24a47d9.png)

# 100行以下で Google の AI を簡単に使えた！
今回は Google AI SDK for Swift を用いて、100行に満たないコード量で Google の AI モデル「Gemini Pro」を使った「チャット」アプリを iPad だけで作成しました。

Google AI SDK for Swift は「チャット」以外に、「テキストのみの入力からテキストを生成」「テキストと画像の入力からテキストを生成」にも対応しています。詳しい情報は Google AI SDK for Swift をご覧ください。

https://github.com/google/generative-ai-swift