SwiftUI中如何让fetchedData类监听DeviceDetails的deviceID属性变化
SwiftUI: 让ObservableObject监听另一个ObservableObject的属性变化
现有代码实现
1. 定义DeviceDetails ObservableObject类
class DeviceDetalis : ObservableObject { @Published var deviceName: String @Published var deviceID: String init(deviceName: String, deviceID: String) { self.deviceName = deviceName self.deviceID = deviceID } }
2. 在App入口设置环境对象
var deviceInfo = DeviceDetalis(deviceName: "", deviceID: "") var body: some Scene { WindowGroup { ContentView() .environmentObject(deviceInfo) } }
3. 视图中使用环境对象接收用户输入
@EnvironmentObject var deviceDetails : DeviceDetalis var body: some View { NavigationView { VStack{ Section { TextField("Device name", text: $deviceDetails.deviceName) .foregroundColor(.black) TextField("Deviceid", text: $deviceDetails.deviceID) .foregroundColor(.black) } .padding() } } }
4. 另一视图展示属性值
@EnvironmentObject var deviceDetails : DeviceDetalis var body: some View { Section { VStack(alignment : .leading) { Text(deviceDetails.deviceName.uppercased()) .fontWeight(.semibold) Text(deviceDetails.deviceID) } } }
需求
需要让另一个ObservableObject类fetchedData监听DeviceDetails的deviceID属性变化,动态更新urlString并调用接口加载数据。当前fetchedData初始代码如下:
class fetchedData : ObservableObject { lazy var urlString = "https://io.adafruit.com/api/v2/Abhishektomar/feeds/\**(deviceID)**" }
解决方案
推荐方案:用Combine订阅监听属性变化
这种方式耦合度低,符合SwiftUI的响应式设计理念。
修改fetchedData类(类名规范首字母大写),注入DeviceDetails实例后,通过Combine订阅deviceID的变化:
import Combine class FetchedData : ObservableObject { // 用@Published让视图感知urlString的变化 @Published var urlString = "" // 存储订阅对象,避免被提前释放 private var cancellables = Set<AnyCancellable>() private let deviceDetails: DeviceDetalis init(deviceDetails: DeviceDetalis) { self.deviceDetails = deviceDetails // 初始化时设置初始URL self.urlString = "https://io.adafruit.com/api/v2/Abhishektomar/feeds/\(deviceDetails.deviceID)" // 订阅deviceID的变化 deviceDetails.$deviceID .receive(on: DispatchQueue.main) .sink { [weak self] newID in guard let self = self else { return } // 更新URL字符串 self.urlString = "https://io.adafruit.com/api/v2/Abhishektomar/feeds/\(newID)" // 触发数据加载逻辑 self.loadData() } .store(in: &cancellables) } // 这里写你的接口请求逻辑 private func loadData() { guard !urlString.isEmpty else { return } // 示例:打印URL,实际替换成网络请求代码 print("开始加载数据:\(urlString)") } }
如何接入到你的项目中
在App入口处,需要同时初始化DeviceDetails和FetchedData,并将两者都设置为环境对象:
@main struct YourAppName: App { let deviceInfo = DeviceDetalis(deviceName: "", deviceID: "") // 注入deviceInfo初始化FetchedData let fetchedData = FetchedData(deviceDetails: deviceInfo) var body: some Scene { WindowGroup { ContentView() .environmentObject(deviceInfo) .environmentObject(fetchedData) } } }
现在当用户修改输入框中的deviceID时,FetchedData会自动感知到变化,更新urlString并触发数据加载。
备选方案:用属性didSet回调(耦合度较高)
如果不想用Combine,可以在DeviceDetails的deviceID属性中添加didSet回调,但这种方式会让两个类耦合度升高:
修改DeviceDetails:
class DeviceDetalis : ObservableObject { @Published var deviceName: String @Published var deviceID: String { didSet { onDeviceIDChanged?(deviceID) } } // 定义回调闭包 var onDeviceIDChanged: ((String) -> Void)? init(deviceName: String, deviceID: String) { self.deviceName = deviceName self.deviceID = deviceID } }
修改FetchedData:
class FetchedData : ObservableObject { @Published var urlString = "" private let deviceDetails: DeviceDetalis init(deviceDetails: DeviceDetalis) { self.deviceDetails = deviceDetails self.urlString = "https://io.adafruit.com/api/v2/Abhishektomar/feeds/\(deviceDetails.deviceID)" // 设置回调 deviceDetails.onDeviceIDChanged = { [weak self] newID in guard let self = self else { return } self.urlString = "https://io.adafruit.com/api/v2/Abhishektomar/feeds/\(newID)" self.loadData() } } private func loadData() { guard !urlString.isEmpty else { return } print("开始加载数据:\(urlString)") } }
接入方式和推荐方案一致,同样需要在App入口注入并设置为环境对象。
内容的提问来源于stack exchange,提问作者KARAN _186
相关产品推荐
相关产品推荐

