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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 11:39:36