如何将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
相关产品推荐
相关产品推荐

