AIでiPhoneアプリ作成する

## 問題点
- リクエストを送った後にトークンを書き換わってしまって再度入力が必要になる

## 説明

このコードは、SwiftUIで書かれたOpenAI APIへのPOSTリクエストを行うプログラムです。

makeOpenAIRequest関数は、OpenAIのAPIキーとニューステキストを受け取り、APIにリクエストを送信して生成されたテキストを返す非同期関数です。

ModalViewは、APIキーを入力するためのモーダルビューです。ユーザーがAPIキーを入力すると、キーが親ビューであるContentViewのtoken変数に保存されます。

ContentViewは、テキストエディタと2つのボタンを持つビューです。1つのボタンは、makeOpenAIRequest関数を呼び出して、APIから生成されたテキストを取得します。もう1つのボタンは、APIキーを入力するためのModalViewを表示します。

ContentViewには、UserDefaultsを使用して、ユーザーが以前に入力したAPIキーを保存するためのコードが含まれています。これにより、アプリケーションが再起動された場合でも、以前に使用されたAPIキーを保持することができます。


## コード本体

```swift

import SwiftUI


import Foundation

func makeOpenAIRequest(news: String, api_key: String, completion: @escaping (String?, Error?) -> Void) {
    let apiKey = api_key
    let urlString = "https://api.openai.com/v1/completions"
    
    guard let url = URL(string: urlString) else {
        print("Invalid URL")
        let error = NSError(domain: "Invalid URL", code: 0, userInfo: nil)
        completion(nil, error)
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
    
    let parameters: [String: Any] = [
        "model": "text-davinci-003",
        "prompt": "" + news,
        "temperature": 0,
        "max_tokens": 100,
        "top_p": 1,
        "frequency_penalty": 0.0,
        "presence_penalty": 0.0,
        "stop": ["\n"]
    ]

    guard let httpBody = try? JSONSerialization.data(withJSONObject: parameters, options: []) else {
        print("Invalid parameters")
        let error = NSError(domain: "Invalid parameters", code: 0, userInfo: nil)
        completion(nil, error)
        return
    }
    
    request.httpBody = httpBody
    
    let configuration = URLSessionConfiguration.default
    configuration.timeoutIntervalForResource = 200  // increase to 2 minutes (120 seconds)
    let session = URLSession(configuration: configuration)
    
    let task = session.dataTask(with: request) { (data, response, error) in
        if let error = error {
            print("Error: \(error.localizedDescription)")
            completion(nil, error)
            return
        }
        
        guard let data = data else {
            print("No data returned")
            let error = NSError(domain: "No data returned", code: 0, userInfo: nil)
            completion(nil, error)
            return
        }
        
        guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any], let choices = json["choices"] as? [[String: Any]], let text = choices.first?["text"] as? String else {
                    // Log the JSON data to aid in debugging.
                    print("Unable to parse JSON: \(String(data: data, encoding: .utf8) ?? "")")
                    let error = NSError(domain: "Unable to parse JSON", code: 0, userInfo: nil)
                    completion(nil, error)
                    return
                }
        
        completion(text, nil)
    }
    
    task.resume()
}



struct ModalView: View {
    @State private var name: String = ""
    @Binding var token: String
    @Binding var showModal: Bool
   
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("User Information")) {
                    TextField("Name", text: $name)
                }
                
                Section {
                    Button(action: {
                        // perform action here
                        
                        self.token = self.name
                        self.showModal = false
                    }) {
                        Text("Submit")
                    }
                }
            }
            .navigationBarTitle("Modal Form")
        }
    }




}





struct ContentView: View {
    @State private var name: String = "a"
    @State private var paragraph: String = "d"
    @State private var text = "Hello, world!!!"
    @State private var profileText = "Hello, world!!!"
    @State private var token = ""
    @State private var showModal: Bool = false
    @State private var isModalPresented = false
    

    
    init() {
            let defaults = UserDefaults.standard
            let savedParagraph = defaults.string(forKey: "savedParagraph")

            if let savedParagraph = savedParagraph {
                self.token = savedParagraph
                print(savedParagraph)
                token = savedParagraph
            }
        }
    var body: some View {
        VStack(spacing: 20) {
            Spacer()
            TextEditor(text: $paragraph)
                .foregroundColor(.secondary)
                .padding(.horizontal)
                .navigationTitle("About you")
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.gray, lineWidth: 2)
                )
            Text(profileText)
                .padding()
            Spacer()
            Button(action: {
                let defaults = UserDefaults.standard
                defaults.set(self.paragraph, forKey: "savedParagraph")
                
                makeOpenAIRequest(news: paragraph, api_key: token) { response, error in
                    if let response = response {
                        DispatchQueue.main.async {
                            self.profileText = response
                        }
                    } else if let error = error {
                        // Handle error
                        print("")
                    }
                }
            }) {
                Text("Generate Text")
            }


            
            .padding()
            .background(Color.accentColor)
            .foregroundColor(.white)
            .cornerRadius(10)
            
            Button(action: {
                self.showModal = true
            }) {
                Text("Enter API Key")
            }
            .sheet(isPresented: $showModal) {
                ModalView(token: self.$token, showModal: self.$showModal)
            }

        }
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


```

## 過程

https://note.com/yukiaofficial/n/n9cd85c56b14d