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

SwiftUI异步问题:sendImage未完成时handleImageSend提前执行

问题原因

核心问题是异步操作执行顺序错误:ImageUploader.sendImage是异步函数,它的completion回调要等图片上传完成、拿到下载URL后才会触发,但你现在在回调外部就直接使用imageURL——此时回调还未执行,imageURL必然为nil。所有依赖该URL的代码必须放在completion闭包内部,确保拿到URL后再执行后续逻辑。

修复方案1:调整Completion回调内的代码逻辑

修改handleImageSend函数,将所有需要用到图片URL的代码移到sendImage的completion闭包中:

func handleImageSend(image: UIImage?) {
    guard let fromId = FirebaseManager.shared.auth.currentUser?.uid else { return }
    guard let chatUser = chatUser else { return }
    guard let image = image else { return }
    
    ImageUploader.sendImage(image: image) { url in
        let document = FirebaseManager.shared.firestore
            .collection("messages")
            .document(fromId)
            .collection(chatUser.uid)
            .document()
        
        print("DEBUG: \(url)")

        let imageMessageData = ["fromId": fromId, "toId": chatUser.uid, "text": url, "timestamp": Timestamp()] as [String : Any]
        
        if !imageMessageData.isEmpty {
            self.messageIsAnImage.toggle()
        }
        
        document.setData(imageMessageData) { error in
            if let error = error {
                print(error.localizedDescription)
                return
            }
        }
        
        self.persistRecentMessage()
        
        let recipientDocument = FirebaseManager.shared.firestore
            .collection("messages")
            .document(chatUser.uid)
            .collection(fromId)
            .document()
        
        recipientDocument.setData(imageMessageData) { error in
            if let error = error {
                print(error.localizedDescription)
                return
            }
        }
    }
}
修复方案2:改用Async/Await简化代码

如果你想用async/await,先把ImageUploader.sendImage改成异步函数,避免回调嵌套:

第一步:修改ImageUploader的sendImage函数

struct ImageUploader {
    // 保留原有uploadImage函数
    
    static func sendImage(image: UIImage) async throws -> String {
        guard let imageData = image.jpegData(compressionQuality: 0.5) else {
            throw NSError(domain: "ImageUploadError", code: 0, userInfo: [NSLocalizedDescriptionKey: "无法将图片转为数据"])
        }
        
        let ref = FirebaseManager.shared.storage.reference(withPath: UUID().uuidString)
        try await ref.putData(imageData)
        let url = try await ref.downloadURL()
        return url.absoluteString
    }
}

第二步:修改handleImageSend为异步函数

func handleImageSend(image: UIImage?) async {
    guard let fromId = FirebaseManager.shared.auth.currentUser?.uid else { return }
    guard let chatUser = chatUser else { return }
    guard let image = image else { return }
    
    do {
        let url = try await ImageUploader.sendImage(image: image)
        
        let document = FirebaseManager.shared.firestore
            .collection("messages")
            .document(fromId)
            .collection(chatUser.uid)
            .document()
        
        print("DEBUG: \(url)")

        let imageMessageData = ["fromId": fromId, "toId": chatUser.uid, "text": url, "timestamp": Timestamp()] as [String : Any]
        
        if !imageMessageData.isEmpty {
            messageIsAnImage.toggle()
        }
        
        try await document.setData(imageMessageData)
        persistRecentMessage()
        
        let recipientDocument = FirebaseManager.shared.firestore
            .collection("messages")
            .document(chatUser.uid)
            .collection(fromId)
            .document()
        
        try await recipientDocument.setData(imageMessageData)
    } catch {
        print("图片上传或消息存储失败:\(error.localizedDescription)")
    }
}

调用时需在异步上下文执行(比如SwiftUI视图中用Task):

// 视图内调用示例
Button("发送图片") {
    if let image = selectedImage {
        Task {
            await viewModel.handleImageSend(image: image)
        }
    }
}

内容的提问来源于stack exchange,提问作者Grant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 01:15:41