简体   繁体   中英

SwiftUI DocumentGroup and switching to background

I have made an app based on the new SwiftUI multi platform target for a "Document based app". However, I face weird issues. As long as an app is in the foreground, it works just fine. If it is moved to the background by task switching, and then again to the foreground, mutations are being saved to the document, but the SwiftUI Views don't receive mutations. So whenever you press a button in the UI that mutates the document you see nothing happening while the mutation is there once you reload the document from disk.

So i am thinking, I use ObservedObjects, they probably get kicked out of memory once I move to the background. could this be the cause of my bug?

But then I added a print line to the App struct.

import SwiftUI

@main
struct MyApp: App {

    fileprivate func myLogging(_ file: FileDocumentConfiguration<MyDocument>) -> some View {
        print("""
                    IT IS CALLED
            """)

        return MainView().environmentObject(BindingWrapper(file.$document))
    }

    var body: some Scene {
        DocumentGroup(newDocument: MyDocument()) { (file) in
            return myLogging(file)
        }.commands { AppCommands() }
    }
}

and guess what... this print always executes just before a mutation is being rendered. Which makes sense. because file.$document is a binding, and if you do a mutating action, the binding will warn Apple that the file is dirty, but it will also invalidate the entire hierarchy. This logging will still print once the bug has occurred!

So on the line MainView().environmentObject(BindingWrapper(file.$document)) I assume everything is created from scratch. BindingWrapper is a custom class I made to convert a binding in an observable object. And this is one of the objects I worried about, that they might be freed. but if they are created newly.... they should be always there, right? And by the way, this object is owned by the environment. So it should not be freed.

So, now I am stuck. is Apple doing some clever caching on bindings / ObservedObjects which will inject old objects into my view hierarchy even though I think everything is created newly?

Try moving any wiring/instantiation to the first view of the document group. If that view houses StateObjects you expect to share the lifetime of the document window, they will not be rebuilt.

In the example below, a WindowStore is housed as an @StateObject as described. A RootStore housed in App creates the WindowStore, which includes vending services and registering it in a managed array of windows. Either could enable your logging service. (For me, that array helps WindowGroups operate on a specific document when @FocusedValue would fail (ie, the top-most document is no longer the key window).)

@main
struct ReferenceFileDoc: App {
    
    @StateObject var root: RootStore
    
    var body: some Scene {
        DocumentGroup { ProjectDocument() } editor: { doc in
            DocumentGroupRoot(
                window: root.makeWindowStore(doc.document),
                factory: SwiftUIFactory(root, doc.document)
            )
            .environmentObject(doc.document)
            .environment(\.documentURL, doc.fileURL)
            .injectStores(from: root)
        }.commands { Menus(root: root) }

    .... other scenes ...
struct DocumentGroupRoot: View {
    
    @EnvironmentObject var doc: ProjectDocument
    @Environment(\.undoManager) var undoManager
    @Environment(\.documentURL) var url

    @StateObject var window: WindowStore
    @StateObject var factory: UIFactory
    
    var body: some View {
        passUndoManagerToDocument()
        factory.reference(window)
        return DocumentWindow(vm: factory.makeThisVM()) // Actual visible window
            .focusedValue(\.keyWindow, window)
            .focusedValue(\.keyDocument, doc)
            .onAppear { /// Tasks }
            .reportHostingNSWindow { [weak window] in
                window?.setWindow($0)
            }
            .onChange(of: url) { [weak window] in window?.setFileURL($0) }
            .environmentObject(/// sub-state stores from WindowStore)
            .environmentObject(window)
            .environmentObject(factory)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM