SwiftData中使用Query与Filter过滤数据时遇编译错误求助
问题解决:SwiftUI视图外使用SwiftData的正确方式
核心问题根源
@Query是SwiftUI专属的属性宏,只能在SwiftUI View结构体/类的顶层作用域中使用,不能在普通工具函数、循环内部等非视图环境下使用。你遇到的三个错误都是因为错误地在函数的循环里使用@Query导致的。
逐个错误解析
- Attribute 'private' can only be used in a non-local scope:
@Query宏展开后会自动生成private修饰的内部变量,而局部作用域(比如函数内的for循环块)不允许定义私有属性,因此触发该错误。 - Cannot convert value of type ... to closure result type:由于
@Query的使用场景错误,宏的展开逻辑异常,导致Predicate表达式无法被正确解析。另外你的fileName是[String]类型,SwiftData对数组的直接相等判断需要确保类型匹配和语法正确。 - Cannot find '_existingItems' in scope:
@Query宏原本会自动生成_existingItems这类内部变量来管理查询结果,但因为使用场景错误,宏展开失败,所以找不到该变量。
修正后的代码实现
直接使用ModelContext的fetch方法来执行查询,替代@Query:
1. 修正FILE模型类
@Model注解的类不需要继承ObservableObject(SwiftData已内置观察机制),可以简化定义:
import Foundation import SwiftData @Model class FILE: Identifiable, Hashable { var fileName: [String] var downloadUrl: URL var sha: String init(fileName: [String], downloadUrl: URL, sha: String) { self.fileName = fileName self.downloadUrl = downloadUrl self.sha = sha } func hash(into hasher: inout Hasher) { hasher.combine(sha) } static func == (lhs: FILE, rhs: FILE) -> Bool { lhs.sha == rhs.sha } }
2. 修正addOrUpdateItems函数
func addOrUpdateItems(_ files: [FILE]) { // 正确创建ModelContainer并处理错误,避免强制解包 guard let container = try? ModelContainer(for: FILE.self) else { print("Failed to create ModelContainer") return } let context = ModelContext(container) for item in files { // 使用FetchDescriptor执行查询,替代@Query let fetchDescriptor = FetchDescriptor<FILE>( predicate: #Predicate<FILE> { $0.fileName == item.fileName } ) do { let existingItems = try context.fetch(fetchDescriptor) if let existingItem = existingItems.first { // 更新已有数据 existingItem.downloadUrl = item.downloadUrl // 如果fileName不需要更新可以省略,这里保持和原逻辑一致 existingItem.fileName = item.fileName } else { // 插入新数据 context.insert(item) } } catch { print("Failed to fetch items: \(error.localizedDescription)") } } // 保存上下文并处理错误 do { try context.save() } catch { print("Failed to save context: \(error.localizedDescription)") } }
额外说明
- 如果你的匹配逻辑不是“数组完全相等”,而是判断数组包含某个特定元素,可以修改Predicate为:
#Predicate<FILE> { $0.fileName.contains(item.fileName.first ?? "") } - 避免使用
try?忽略所有错误,建议在生产代码中用do-catch捕获并处理具体错误,便于调试。
内容的提问来源于stack exchange,提问作者kiddslopp
相关产品推荐
相关产品推荐

