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

如何将Swift中的createUsers函数改造为抛出(throws)函数?

没问题,我来帮你把这个createUsers函数改成支持throws的版本——这样你就能轻松捕获错误并展示对应的UIAlertController给用户了。

首先要说明:原函数是异步操作(Firebase实时监听+网络请求),传统闭包回调没法直接用throws,所以我们用Swift的async/await并发模型配合AsyncThrowingStream来实现,这样既能保留实时监听的特性,又能通过抛出机制传递错误。


1. 优化错误枚举(增强可展示的错误信息)

首先把你的networkError枚举升级一下,让它携带具体错误详情并支持本地化描述,这样直接就能用在Alert里:

enum NetworkError: Error, LocalizedError {
    case invalidURL(String)
    case invalidUserInfo(String)
    case invalidData(String)
    case standard(Error)
    case nilProperty(String)
    
    // 实现LocalizedError,直接返回友好的错误描述
    var errorDescription: String? {
        switch self {
        case .invalidURL(let url):
            return "无效的图片URL: \(url)"
        case .invalidUserInfo(let uid):
            return "用户\(uid)的信息格式不正确"
        case .invalidData(let uid):
            return "获取用户\(uid)的图片数据失败"
        case .standard(let error):
            return "网络请求错误: \(error.localizedDescription)"
        case .nilProperty(let property):
            return "缺少必要属性: \(property)"
        }
    }
}

2. 封装Firebase实时监听为异步流

把Firebase的observe(.childAdded)监听包装成AsyncThrowingStream,这样我们就能用async/await的方式处理实时事件:

private func userSnapshots() -> AsyncThrowingStream<DataSnapshot, Error> {
    AsyncThrowingStream { continuation in
        // 启动监听,获取句柄方便后续取消
        let observerHandle = REF_USER.observe(.childAdded) { snapshot in
            continuation.yield(snapshot)
        }
        
        // 当流终止时,移除Firebase监听,避免内存泄漏
        continuation.onTermination = { @Sendable _ in
            REF_USER.removeObserver(withHandle: observerHandle)
        }
    }
}

3. 改造createUsers为支持throws的异步流函数

现在重写createUsers,把原来的闭包逻辑换成异步+抛出错误的方式:

func createUsers() -> AsyncThrowingStream<User, Error> {
    AsyncThrowingStream { continuation in
        Task {
            do {
                // 遍历实时到来的用户快照
                for try await snapshot in userSnapshots() {
                    let uid = snapshot.key
                    
                    // 解析用户字典,失败则抛出错误
                    guard let userDict = snapshot.value as? [String: String] else {
                        throw NetworkError.invalidUserInfo(uid)
                    }
                    
                    // 检查必要属性,缺失则抛出错误
                    guard let fullname = userDict["fullname"] else {
                        throw NetworkError.nilProperty("用户\(uid)的fullname")
                    }
                    
                    guard let profileImageUrlString = userDict["profileImageUrl"] else {
                        throw NetworkError.nilProperty("用户\(uid)的profileImageUrl")
                    }
                    
                    // 验证URL格式,失败则抛出错误
                    guard let profileImageUrl = URL(string: profileImageUrlString) else {
                        throw NetworkError.invalidURL(profileImageUrlString)
                    }
                    
                    // 用async/await发起网络请求,失败会自动抛出错误
                    let (imageData, response) = try await URLSession.shared.data(from: profileImageUrl)
                    
                    // 可选:检查HTTP响应状态码
                    guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
                        throw NetworkError.invalidData(uid)
                    }
                    
                    // 解析图片,失败则抛出错误
                    guard let profileImage = UIImage(data: imageData) else {
                        throw NetworkError.invalidData(uid)
                    }
                    
                    // 创建用户对象并发送到流中
                    let user = User(uid: uid, fullname: fullname, profileImage: profileImage, isFollowed: false)
                    continuation.yield(user)
                }
            } catch {
                // 遇到错误时终止流并抛出错误
                continuation.finish(throwing: error)
            }
        }
    }
}

4. 使用示例(在UI层处理错误并展示Alert)

现在你可以在ViewController里这样使用,轻松捕获错误并展示Alert:

func startFetchingUsers() {
    Task {
        do {
            // 遍历异步流中的用户,实时更新UI
            for try await user in createUsers() {
                DispatchQueue.main.async {
                    // 比如更新用户列表并刷新表格
                    self.users.append(user)
                    self.tableView.reloadData()
                }
            }
        } catch let error as NetworkError {
            // 捕获自定义错误,展示Alert
            DispatchQueue.main.async {
                let alert = UIAlertController(
                    title: "加载用户失败",
                    message: error.localizedDescription,
                    preferredStyle: .alert
                )
                alert.addAction(UIAlertAction(title: "确定", style: .default))
                self.present(alert, animated: true)
            }
        } catch {
            // 处理其他未知错误
            DispatchQueue.main.async {
                let alert = UIAlertController(
                    title: "加载失败",
                    message: "发生未知错误,请稍后重试",
                    preferredStyle: .alert
                )
                alert.addAction(UIAlertAction(title: "确定", style: .default))
                self.present(alert, animated: true)
            }
        }
    }
}

这样改造后,所有可能失败的步骤都会抛出明确的错误,你可以直接把错误描述展示给用户,完全符合你的需求。

内容的提问来源于stack exchange,提问作者Mattias Törnqvist

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 09:22:54