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

如何在Core Data中使用FetchedResults填充数组 SwiftUI MVVM场景问题

问题原因
  • @Environment、@FetchRequest 是SwiftUI专用的属性包装器,仅能在View结构体中生效,在ObservableObject类中无法完成依赖注入、也无法执行查询逻辑,所以你拿到的artists1永远是空集合
  • 就算在View中使用@FetchRequest,初始化阶段查询也尚未执行,此时直接访问结果为空,需要等待View的body渲染周期触发后才能拿到数据,不适合放到仓储类的初始化逻辑里
正确实现方案

你需要手动持有Core Data上下文,自行构造查询请求执行获取,同时监听Core Data的变更自动更新数据,修改后的完整代码如下:

import Foundation
import SwiftUI
import CoreData
import Combine

class SongRepository: ObservableObject, Identifiable {
    @Published var songs = [Song]()
    private let managedObjectContext: NSManagedObjectContext
    private var cancellables = Set<AnyCancellable>()
    
    // 初始化时传入Core Data上下文,不要用@Environment注入
    init(context: NSManagedObjectContext) {
        self.managedObjectContext = context
        // 监听Core Data上下文的变更,数据改动后自动刷新
        NotificationCenter.default.publisher(
            for: NSManagedObjectContext.didSaveObjectsNotification,
            object: managedObjectContext
        )
        .receive(on: DispatchQueue.main)
        .sink { [weak self] _ in
            self?.loadData()
        }
        .store(in: &cancellables)
        
        // 首次加载数据
        loadData()
    }
    
    func loadData() {
        // 手动构造FetchRequest执行查询,不要用@FetchRequest
        let fetchRequest: NSFetchRequest<Artists> = Artists.fetchRequest()
        fetchRequest.sortDescriptors = [] // 按需添加排序规则即可
        
        do {
            let artists = try managedObjectContext.fetch(fetchRequest)
            // 转换为自定义的Song模型
            songs = artists.map { artist in
                Song(
                    album: artist.album,
                    artistImage: artist.artistImage,
                    artistname: artist.artistname ?? "",
                    genre: artist.genre,
                    songMp3: artist.songMp3,
                    songname: artist.songname
                )
            }
        } catch {
            print("Core Data查询失败: \(error)")
        }
    }
}
使用方式

在你的SwiftUI App入口或者上层View中初始化仓储类,传入项目里已有的Core Data上下文即可,示例:

@main
struct YourApp: App {
    // 这里用你项目中已有的持久化控制器实例即可
    let persistenceController = PersistenceController.shared
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
                // 将仓储类作为环境对象注入,所有子View都可以直接获取使用
                .environmentObject(SongRepository(context: persistenceController.container.viewContext))
        }
    }
}
注意事项
  • 原代码中对artistname做了强制解包,建议替换为可选值默认值,避免Core Data中该字段为空时触发崩溃
  • 如果需要添加查询筛选条件,直接给fetchRequest.predicate赋值对应的NSPredicate即可
  • 后续所有Core Data读写操作都可以封装在该仓储类中,对外仅暴露@Published的songs数组,完全符合MVVM的单向数据流要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 13:27:04