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

如何让网络层持续更新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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 10:17:47