如何让网络层持续更新ObservableObject中的@Published变量?
从网络层更新@Published变量的正确方式
在SwiftUI的ObservableObject中,要让网络层持续更新@Published变量,核心是让网络层能回调到Service层更新状态,下面是几种实用的实现方式:
方案一:传递状态更新闭包(最直接)
直接给网络层的方法传入一个闭包,网络请求的每一步调用这个闭包更新状态,同时注意必须在主队列更新@Published变量(因为UI状态更新需要主线程)。
NetworkManager 实现
class NetworkManager { static func doNetwork(updateStatus: @escaping (String) -> Void) async throws { // 模拟网络请求各阶段 updateStatus("开始请求...") try await Task.sleep(nanoseconds: 1_000_000_000) updateStatus("正在获取数据...") try await Task.sleep(nanoseconds: 1_000_000_000) updateStatus("请求完成") } }
SomethingService 调用
class SomethingService: ObservableObject { @Published var statusString: String = "" func doNetwork() async { do { try await NetworkManager.doNetwork { [weak self] status in // 切换到主队列更新UI状态 DispatchQueue.main.async { self?.statusString = status } } } catch { DispatchQueue.main.async { self?.statusString = "请求失败: \(error.localizedDescription)" } } } }
注意用
[weak self]避免循环引用,防止内存泄漏。
方案二:使用AsyncSequence返回状态流(符合Swift并发风格)
让网络层返回一个异步序列(AsyncThrowingStream),Service层遍历这个序列来更新状态,这种写法更贴合Swift并发的设计理念。
NetworkManager 实现
class NetworkManager { static func doNetwork() -> AsyncThrowingStream<String, Error> { AsyncThrowingStream { continuation in Task { do { // 依次发送各阶段状态 continuation.yield("开始请求...") try await Task.sleep(nanoseconds: 1_000_000_000) continuation.yield("正在获取数据...") try await Task.sleep(nanoseconds: 1_000_000_000) continuation.yield("请求完成") continuation.finish() } catch { continuation.finish(throwing: error) } } } } }
SomethingService 调用
class SomethingService: ObservableObject { @Published var statusString: String = "" func doNetwork() async { do { // 遍历异步序列更新状态 for try await status in NetworkManager.doNetwork() { DispatchQueue.main.async { self.statusString = status } } } catch { DispatchQueue.main.async { self.statusString = "请求失败: \(error.localizedDescription)" } } } }
方案三:Delegate模式(适合复杂场景)
定义一个状态更新协议,让Service遵守该协议作为代理,网络层通过代理回调更新状态,适合多场景复用的复杂网络逻辑。
定义代理协议
protocol NetworkStatusDelegate: AnyObject { func updateNetworkStatus(_ status: String) }
NetworkManager 实现
class NetworkManager { static func doNetwork(with delegate: NetworkStatusDelegate) async throws { delegate.updateNetworkStatus("开始请求...") try await Task.sleep(nanoseconds: 1_000_000_000) delegate.updateNetworkStatus("正在获取数据...") try await Task.sleep(nanoseconds: 1_000_000_000) delegate.updateNetworkStatus("请求完成") } }
SomethingService 实现
class SomethingService: ObservableObject, NetworkStatusDelegate { @Published var statusString: String = "" func doNetwork() async { do { try await NetworkManager.doNetwork(with: self) } catch { DispatchQueue.main.async { self.statusString = "请求失败: \(error.localizedDescription)" } } } // 实现代理方法 func updateNetworkStatus(_ status: String) { DispatchQueue.main.async { self.statusString = status } } }
核心注意点
所有更新@Published变量的操作必须放在主队列,因为SwiftUI的视图更新依赖主线程,后台线程直接更新可能导致UI异常。
内容的提问来源于stack exchange,提问作者erotsppa
相关产品推荐
相关产品推荐

