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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:55:21