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

iOS 17中如何用Swift并发与Observation框架监听搜索栏值变化

iOS 17 @Observable 监听搜索文本变化的正确方式

在iOS 17的@Observable框架中,属性并没有直接提供类似@Published的$values异步序列,所以直接使用searchText.async无法监听到变化。下面提供两种可靠的实现方式:

方法一:结合Combine PassthroughSubject(兼容旧习惯)

利用Combine的PassthroughSubject转发searchText的变化,再转换成AsyncStream供并发监听,逻辑和你之前用@Published的写法最接近:

import Combine

@Observable
final class SearchableViewModel {
    // 用@ObservationIgnored标记,避免Observation框架跟踪该Subject
    @ObservationIgnored
    private let searchTextSubject = PassthroughSubject<String, Never>()
    
    var searchText: String = "" {
        didSet {
            searchTextSubject.send(searchText)
        }
    }
    
    func listenToSearchQuery() async {
        // 将Subject转换为AsyncStream进行监听
        for await searchQuery in searchTextSubject.values {
            print(searchQuery)
            // 可选:添加防抖逻辑,避免输入过程中频繁触发
            // try? await Task.sleep(nanoseconds: 300_000_000)
            // if searchQuery == self.searchText { ... }
        }
    }
}

视图代码无需修改,保持你原来的写法即可。

方法二:纯Observation框架实现(无Combine依赖)

如果不想依赖Combine,可使用withObservationTracking构建AsyncStream来监听属性变化:

@Observable
final class SearchableViewModel {
    var searchText: String = ""
    
    // 创建监听searchText变化的AsyncStream
    private func searchTextStream() -> AsyncStream<String> {
        AsyncStream { continuation in
            // 监听应用退后台事件,防止内存泄漏
            let backgroundTask = Task {
                for await _ in NotificationCenter.default.notifications(named: UIApplication.willResignActiveNotification) {
                    continuation.finish()
                }
            }
            
            continuation.onTermination = { @Sendable _ in
                backgroundTask.cancel()
            }
            
            // 启动Observation跟踪,递归调用持续监听下一次变化
            func trackChanges() {
                withObservationTracking {
                    _ = self.searchText
                } onChange: {
                    continuation.yield(self.searchText)
                    trackChanges()
                }
            }
            
            trackChanges()
        }
    }
    
    func listenToSearchQuery() async {
        for await searchQuery in searchTextStream() {
            print(searchQuery)
        }
    }
}

额外优化:防抖处理

搜索场景通常需要防抖,避免输入过程中频繁触发逻辑,可在监听代码中添加延迟判断:

func listenToSearchQuery() async {
    for await searchQuery in searchTextSubject.values {
        try? await Task.sleep(nanoseconds: 300_000_000)
        // 仅当当前文本与触发时的文本一致时,才执行后续逻辑(避免处理过时输入)
        if searchQuery == self.searchText {
            print("处理搜索请求:\(searchQuery)")
        }
    }
}

内容的提问来源于stack exchange,提问作者svranaw

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:20:29