当更改源是共享扩展时,@FetchRequest 结果不会在 SwiftUI 中更新

dro*_*ang 11 core-data ios swift swiftui

@FetchRequest 上有很多问题的答案提供了强制 SwiftUI 重绘视图的解决方法。我找不到解决这个问题的任何内容。

我的应用程序使用共享扩展将新项目插入到我的CoreData模型 (sqlite) 中。框架在主应用程序和共享扩展目标之间共享。ContentView我的应用程序中的主要 SwiftUI使用一个@FetchRequest属性来监视数据库的内容并更新视图:

struct ContentView: View {
    @FetchRequest(fetchRequest: MyObj.allFetchRequest()) var allObjs: FetchedResults<MyObj>
    ...
}
Run Code Online (Sandbox Code Playgroud)

我的 xcdatamodel 扩展为:

    public static func allFetchRequest() -> NSFetchRequest<MyObj> {
        let request: NSFetchRequest<MyObj> = MyObj.fetchRequest()
        // update request to sort by name
        request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        return request
    }
Run Code Online (Sandbox Code Playgroud)

共享扩展使用以下方法添加一个新项目:

let newObj = MyObj(context: context)
newObj.name = "new name"
try! context.save()
Run Code Online (Sandbox Code Playgroud)

使用共享扩展添加新项目后,当我通过在设备上切换应用程序返回到我的应用程序时,SceneDelegate检测到sceneDidBecomeActive. 在这个函数中,我可以手动运行一个 fetch 请求并看到新项目已经添加。我什至可以使用其他 SO 问题的答案来诱使 UI 在 ContentView 中重建(通过在收到通知时切换构建器中使用的 Bool),但在任何一种情况下,当 UI 重建时,allObjs都不包含来自数据库。

在我的文章中,sceneDidBecomeActive我尝试了多种代码组合,以尝试使用数据库的最新内容以一种@FetchRequest可以看到它的方式更新上下文。数据在那里,我的托管上下文可以看到这个新数据,因为手动获取请求返回新项目,但@FetchRequest总是过时阻止 UI 重建。在这两个位置,我都打印了上下文,并且确信它是相同的上下文。我试过了:

  • context.reset()
  • context.refreshAllObjects()
  • context.save()

我不知道为什么@FetchRequestContentView重绘时不更新其值。

@FetchRequest在我的共享扩展将新对象添加到数据库后,如何强制更新其对象并导致 SwiftUI 重建?

(Xcode 11.4.1,Swift 5,目标:iOS 13.1)

编辑:

在 my 中ContentView,我打印allObjs变量、它们的状态、上下文的内存地址等。我将其与从上下文中新提取的值进行比较,并且现有对象的所有值都相同,除了新提取显示新添加的对象,而allObjs没有包含新的对象。很明显,该@FetchRequest对象没有得到更新。

编辑2:

我尝试创建一个ObservableObject带有@Published属性和显式update()方法的自定义类,然后将其用作@ObservedObjectin ContentView。这有效。

class MyObjList: ObservableObject {
    static let shared = MyObjList()
    @Published var allObjs: [MyObj]!

    init() {
        update()
    }

    func update() {
        allObjs = try! DataStore.persistentContainer.viewContext.fetch(MyObj.allFetchRequest())
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在ContentView

@EnvironmentObject var allObjs: MyObjList
Run Code Online (Sandbox Code Playgroud)

这有助于确认数据在那里,只是没有被@FetchRequest. 这是一个功能性的解决方法。

Asp*_*eri -1

如果适用于您的应用程序,请尝试移动ContentView应用程序上的创建转到前台(因此将从头开始重新创建获取请求)

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

    // leave here only window creation
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        self.window = window
    }
}

// ... other code here

func sceneWillEnterForeground(_ scene: UIScene) {

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    let contentView = ContentView().environment(\.managedObjectContext, context)
    let controller = UIHostingController(rootView: contentView)

    window?.rootViewController = controller
    window?.makeKeyAndVisible()
}
Run Code Online (Sandbox Code Playgroud)