You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 16:53:08