[Swift] ホラー画像生成アプリ

# はじめに

だいぶ暑くなってきましたね。
これから夏本番ということで、今回は、Swiftを使ってホラーテイストな画像を生成するiOSアプリを作ってみました👻

# 要件

* iOS 14.0以降
* Xcode 12.0以降
* OpenAI APIキー

# 環境構築

1. **Xcodeで新規プロジェクトを作成**

   * プロジェクト名: HorrorImageConverter
   * インターフェース: SwiftUI
   * 言語: Swift

2. **OpenAI APIキーを取得**

OpenAI API ([https://openai.com/index/openai-api/](https://openai.com/index/openai-api/)) でアカウントを作成し、APIキーを取得してください。

# 要件
- 作りたい画像のイメージを入力できる
- 画像生成ボタンをタップすると、ホラー画像が生成される

# 実装

```swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State private var inputText = ""
    @State private var outputImage: UIImage?
    @State private var showAlert = false
    @State private var isLoading = false // ローディング状態を管理する状態変数

    var body: some View {
        VStack {
            // 入力フィールド
            TextField("イメージを入力してください...", text: $inputText)
                .padding()
                .border(Color.gray, width: 1)

            // 画像生成ボタン
            Button("画像生成") {
                self.outputImage = nil // 画像を非表示にする
                isLoading = true // ボタンが押されたらローディング開始
                generateImage(from: inputText)
            }
            .padding()
            .border(Color.gray, width: 1)
            .disabled(isLoading) // ローディング中はボタンを無効化

            // ローディングアイコン
            if isLoading {
                ProgressView()
                    .progressViewStyle(CircularProgressViewStyle())
            }

            // 生成された画像を表示
            if let outputImage = outputImage {
                Image(uiImage: outputImage)
                    .resizable()
                    .scaledToFit()
                    .frame(maxHeight: 300)
            }
        }
        .padding()
        .alert(isPresented: $showAlert) {
            Alert(title: Text("エラー"), message: Text("画像の生成に失敗しました。"), dismissButton: .default(Text("OK")))
        }
    }

    func generateImage(from text: String) {
        let apiKey = "sk-proj-xxxxx"
        let url = URL(string: "https://api.openai.com/v1/images/generations")!

        // ホラーテイストを必ず入れるためのプロンプト調整
        let horrorPrompt = "An eerie and unsettling scene with \(text), in the style of a horror movie poster, dark and gruesome, high quality, digital art."

        let parameters: [String: Any] = [
            "model": "dall-e-3", // dall-e-3に指定
            "prompt": horrorPrompt,
            "n": 1,
            "size": "1024x1024" // 画像サイズ
        ]

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try? JSONSerialization.data(withJSONObject: parameters)

        URLSession.shared.dataTask(with: request) { data, response, error in
            // エラー処理
            if let error = error {
                print("エラー: \(error)")
                DispatchQueue.main.async {
                    self.showAlert = true
                }
                return
            }

            guard let data = data else {
                print("データがありません")
                DispatchQueue.main.async {
                    self.showAlert = true
                }
                return
            }

            do {
                if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
                   let dataArray = json["data"] as? [[String: Any]],
                   let firstImageData = dataArray.first,
                   let imageUrlString = firstImageData["url"] as? String,
                   let imageUrl = URL(string: imageUrlString) {
                   
                    // 画像の取得
                    if let imageData = try? Data(contentsOf: imageUrl), let image = UIImage(data: imageData) {
                        DispatchQueue.main.async {
                            self.outputImage = image
                            self.isLoading = false
                        }
                    } else {
                        print("画像の取得またはデコードエラー")
                        DispatchQueue.main.async {
                            self.showAlert = true
                        }
                    }
                } else {
                    print("画像URLの解析エラー")
                    DispatchQueue.main.async {
                        self.showAlert = true
                    }
                }

            } catch {
                print("JSON解析エラー: \(error)")
                DispatchQueue.main.async {
                    self.showAlert = true
                }
            }
        }.resume()
    }
}

```

※今回はローカルで動かすための簡易実装です。
※APIキーの取り扱いには、十分にご注意ください。

# 動作確認
女の子
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/ffce0c3a-5ebb-8fb2-ab5b-3f0265d1664a.png" width="300px" />

チューリップ
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/6f9efe70-3b32-1c45-ceb0-c9b3b57ea00f.png" width="300px" />

笑顔
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/d90a3462-3911-6782-b5d8-b5e287f904fc.png" width="300px" />

お花畑
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407770/5e8da339-7dc5-2531-9e6e-402ad2e44f92.png" width="300px" />

どれも怖いですね、、、👻

# まとめ

今回は、OpenAI APIを使ってホラー画像を生成するiOSアプリを開発しました。画像生成AIの技術は日々進化しており、アイデア次第で様々なアプリ開発に活用できます。

ぜひ、自分だけのオリジナルアプリ開発に挑戦してみてください！