SwiftUI开发ChatGPT克隆:动画提示与字符串追加异常问题
ChatGPT克隆应用问题修复:消息重复与响应截断
问题描述
我开发了一个ChatGPT克隆应用,目前遇到两个问题:
- 发送消息后,输入的问题会循环重复显示
- API返回的响应内容被截断
问题截图:
相关代码:
import SwiftUI import OpenAISwift final class ViewModel: ObservableObject{ init(){} private var client : OpenAISwift? func setup(){ client = OpenAISwift(authToken: "sk-") } func send(text:String,completion:@escaping (String) -> Void ){ self.client?.sendCompletion(with: text, maxTokens:500,completionHandler: {result in switch result { case .success(let model): let output = model.choices.first?.text ?? "" completion(output) case .failure: break } }) } } struct ContentView: View { @ObservedObject var viewModel = ViewModel() @State var messageText = "" @State var messages = [String]() @State private var animating: Bool = false var body: some View { VStack(alignment: .leading) { AnimatedTextView(text: messages.joined(separator: "\n")) .frame(height: 100) .frame(maxWidth: .infinity, alignment: .leading) Spacer() HStack { TextField("type here ...", text: $messageText) Button("Send") { send() } } } .onAppear { viewModel.setup() } .padding() } private func send() { guard !messageText.trimmingCharacters(in: .whitespaces).isEmpty else { return } messages.append(messageText) viewModel.send(text: messageText) { response in DispatchQueue.main.async { self.messages.append("ChatGPT: " + response) animating = true } } } } struct AnimatedTextView: View { let text: String @State private var letters: [String] = [] @State private var displayText: String = "" var body: some View { Text(displayText) .font(.system(size: 20)) .frame(height: 100) .frame(maxWidth: .infinity, alignment: .leading) .onChange(of: text) { _ in letters = Array(text).map { String($0) } loop() } } private func loop() { let pace = 0.05 if !letters.isEmpty { DispatchQueue.main.asyncAfter(deadline: .now() + pace) { displayText += letters.removeFirst() loop() } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
问题修复方案
1. 解决消息循环重复问题
原因:AnimatedTextView监听文本变化时,没有重置已显示的内容,每次新消息加入后,会把所有历史消息重新追加一遍,导致重复。
修复代码:修改AnimatedTextView的onChange逻辑,先清空显示文本再重新加载:
.onChange(of: text) { newValue in displayText = "" // 重置显示内容 letters = Array(newValue).map { String($0) } loop() }
同时可以移除无用的animating变量,精简代码。
2. 解决API响应截断问题
原因:当前使用的sendCompletion是文本续写接口,不是专门的对话接口;且maxTokens设置的是输入+输出的总token数,限制了输出长度。
修复步骤:
- 改用Chat Completion对话接口,适配多轮对话场景
- 调整
maxTokens为仅控制输出长度,设置合理数值 - 处理请求失败的情况,给用户反馈
修改ViewModel的send方法:
func send(text: String, completion: @escaping (String) -> Void) { // 构造对话消息,可扩展为包含历史上下文 let messages = [ChatMessage(role: .user, content: text)] self.client?.sendChat(with: messages, maxTokens: 1000, completionHandler: { result in switch result { case .success(let model): let output = model.choices.first?.message.content ?? "" completion(output) case .failure(let error): print("请求失败:\(error)") completion("请求出错,请稍后重试") } }) }
额外优化
- 发送后清空输入框,提升体验:在
send方法末尾添加messageText = "" - 改用结构化的消息存储,方便后续扩展多轮对话:
// 在ContentView中添加 struct ChatMessageItem: Identifiable { let id = UUID() let role: String // "user"或"assistant" let content: String } @State var messages = [ChatMessageItem]() // 修改send方法 private func send() { let trimmedText = messageText.trimmingCharacters(in: .whitespaces) guard !trimmedText.isEmpty else { return } messages.append(ChatMessageItem(role: "user", content: trimmedText)) viewModel.send(text: trimmedText) { response in DispatchQueue.main.async { self.messages.append(ChatMessageItem(role: "assistant", content: "ChatGPT: " + response)) } } messageText = "" }
内容的提问来源于stack exchange,提问作者NinjaDeveloper
相关产品推荐
相关产品推荐

