FREE TRIAL: Accelerate your app development career with Hacking with Swift+! >>

SOLVED: Core Data not persisting data

Forums > macOS

I am new to SwiftUI and Coredata. I am working on an editor using NSAttributableString in an NSRepresentableView encapsulating an NSView.

The sequence is:

I created a "project" (in my app - Hermes) which has many different document types. The documents are listed in a left hand sidebar. Selecting one of the documents loads the center view (Document) with that text and the right sidebar (inspector) shows information about that document. (similar layout to XCode)

The documents are created and stored in CoreData when the user selects "Create new project" from the initial project screen.

That works correctly and the data is saved in Core Data as expected. The main screen is then displayed with the various documents listed in the left hadn Sidebar. The user then selects a sidebar item (document) and the text for that document is displayed.

The user then edits the text (type, change, delete, etc.) as the typing is done (not efficient, just for now) the DocumentView is called back as a delegate of the NSRepresentableView "coordinator". There the change is saved to CoreData as each letter or change is entered.

I print out a series of debugging prints tracing the location and the contents of the attrributedString.

I can see the string sent to the Document.textEdited(...) func. I check that the MOC viewContext has no changes, then I assign the updated string to the ManagedObject (sidebarItem.document?.text = attrString) and attempt to save it back using context.save().

The attempt reports that is recognizes the context has changes and it says it successfully saves the update. However, looking in the SQL store (using Core Data Lab) I can see the data is not updated. If I switch to another document and back, the changes appears to be in memory, but If I stop the app and restart it, the changes are lost. I can verify the changes are not reflected in the SQL store by using Core Data Lab to view the entities in the SQL tables.

If it would help to see/use the entire app it is in github at:

Hermes

ANY suggestions on how to fix or even debug this would be appreciated. I have been trying to solve this for over a week and have refactored the code several times and I can not see where the issue is.

Here is the clip of added save() in Persistence.swift - standard from App with SwftUI Template

    public static func saveContext () {
        print("\n---> saveContext()")
        let context = shared.container.viewContext
        if context.hasChanges {
            print("\n----> viewContext has changes - try context.save()")
            do {
                try context.save()
                print("\n----> viewContext saved changes")
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        } else {
            print("\n----> viewContext does not have changes")
        }
    }

Here is the clip from the code initializing the CD store:

    fileprivate func LoadSidebarChildren(children: [SidebarJSONModel], parent: Sidebar? = nil) {

        children.forEach { childJSON in

            let doc = Document(context: viewContext)
            doc.id = UUID()
            doc.text = createAttributedString(string: "\(childJSON.name)")

            let sidebarItem = Sidebar(context: viewContext)

            sidebarItem.id = UUID()
            sidebarItem.iconName = childJSON.iconName
            sidebarItem.index = seqNo; seqNo += 1
            sidebarItem.name = childJSON.name
            sidebarItem.type = childJSON.type
            sidebarItem.document = doc

            sidebarItem.parent = parent

            PersistenceController.saveContext()

            if sidebarItem.type == "Project" {
                projectSidebarItem = sidebarItem
            }

            if let children = childJSON.children {
                LoadSidebarChildren(children: children, parent: sidebarItem)
            }
        }
    }
    func createAttributedString(string: String) -> NSAttributedString {

        let paragraph = NSMutableParagraphStyle()
        paragraph.alignment = .center

        let astr = NSMutableAttributedString(string: "\(string): ", attributes: [:])

        let a1 = [NSAttributedString.Key.font: NSFont.boldSystemFont(ofSize: 18),
                  NSAttributedString.Key.foregroundColor: NSColor.systemBlue]

        astr.append(NSAttributedString(string: "Hello ", attributes: a1))

        let a2 = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: 32),
                  NSAttributedString.Key.foregroundColor: NSColor.systemGreen]

        astr.append(NSAttributedString(string: "World!", attributes: a2))

        astr.addAttributes([.paragraphStyle: paragraph],
                           range: NSMakeRange(0, (astr.string as NSString).length))

        return astr
    }
}

Here is the code in DocumentView sending the text to the NSRepresentableView, and the teztedit func that is called back.

struct DocumentView: View {

    let sidebar: Sidebar

    @EnvironmentObject var currentSidebar: oCurrentSidebar

    var body: some View {

        print("\n\n_____________________________________________________\nDocumentView(\(sidebar.name)) - Document Selected")

        var attrString = NSAttributedString()

        if let attrStr = sidebar.document?.text {
            print("\nDocumwntView: [\(attrStr)]\n")
            attrString = attrStr
        }
        if currentSidebar.sidebar != sidebar {
            currentSidebar.sidebar = sidebar
        }

        return GeometryReader { geometry in

            NavigationView {
                VStack {
                    VStack {

                        Divider()

                        CustomTextEditor(delegate: self, attrString: attrString)
                    }
                    .background(Color.white)

                    DocumentFooterView()
                }
                .frame( minWidth: geometry.size.width * 0.5, idealWidth: geometry.size.width * 0.75 )
                .frame( maxWidth:.infinity, maxHeight: .infinity )

                InspectorView()
            }
        }

    }
    func textEdited( attrString: NSAttributedString ) {

        // first save attempt is just a check to verify no changes exist in the context yet. This is just for debugging
        //
        print("\n\n-> Document.textEdit(sidebar.document.text - before assignment : [\(String(describing: sidebar.document?.text))]")
        PersistenceController.saveContext()

        sidebar.document?.text = attrString

        print("\n\n-> Document.textEdit(sidebar.document.text - after assignment : [\(String(describing: sidebar.document?.text))\n")
        PersistenceController.saveContext()
    }
}

Below is the sample printouts. (edit is to the word "World!" - it starts World! and is edited to be World! a)

// coordinator in NSRepresentableView
//
--> coordinator.textDidChange(Manuscript: {
    NSFont = "\"Helvetica 12.00 pt. P [] (0x7fd23d580ba0) fobj=0x7fd20d4068a0, spc=3.33\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
}Hello {
    NSColor = "Catalog color: System systemBlueColor";
    NSFont = "\".AppleSystemUIFontBold 18.00 pt. P [] (0x7fd20d50f4f0) fobj=0x7fd20d50f4f0, spc=4.15\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
}World! a{
    NSColor = "Catalog color: System systemGreenColor";
    NSFont = "\".AppleSystemUIFont 32.00 pt. P [] (0x7fd20d512210) fobj=0x7fd20d512210, spc=6.97\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
}

// Coordinator calls document.txtEdit
//
-> Document.textEdit(sidebar.document.text - before assignment : [Optional(Manuscript: {
    NSFont = "\"Helvetica 12.00 pt. P [] (0x7fd23d580ba0) fobj=0x7fd20d4068a0, spc=3.33\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
}Hello {
    NSColor = "Catalog color: System systemBlueColor";
    NSFont = "\".AppleSystemUIFontBold 18.00 pt. P [] (0x7fd20d50f4f0) fobj=0x7fd20d50f4f0, spc=4.15\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
}World! a{
    NSColor = "Catalog color: System systemGreenColor";
    NSFont = "\".AppleSystemUIFont 32.00 pt. P [] (0x7fd20d512210) fobj=0x7fd20d512210, spc=6.97\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
})]

---> saveContext()

----> viewContext does not have changes

-> Document.textEdit(sidebar.document.text - after assignment : [Optional(Manuscript: {
    NSFont = "\"Helvetica 12.00 pt. P [] (0x7fd22d411a20) fobj=0x7fd20d4068a0, spc=3.33\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
}Hello {
    NSColor = "Catalog color: System systemBlueColor";
    NSFont = "\".AppleSystemUIFontBold 18.00 pt. P [] (0x7fd20d50f4f0) fobj=0x7fd20d50f4f0, spc=4.15\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
}World! a{
    NSColor = "Catalog color: System systemGreenColor";
    NSFont = "\".AppleSystemUIFont 32.00 pt. P [] (0x7fd20d512210) fobj=0x7fd20d512210, spc=6.97\"";
    NSParagraphStyle = "Alignment 2, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n    28L,\n    56L,\n    84L,\n    112L,\n    140L,\n    168L,\n    196L,\n    224L,\n    252L,\n    280L,\n    308L,\n    336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection -1, HyphenationFactor 0, TighteningForTruncation YES, HeaderLevel 0 LineBreakStrategy 0";
})

---> saveContext()

----> viewContext has changes - try context.save()

----> viewContext saved changes

And finally, here is the beginning of the data in the SQL store, note it does not change/update. And the "a" is not saved.

{
    "$archiver" = NSKeyedArchiver;
    "$objects" =     (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x600001a84820 [0x7fff8007d050]>{value = 35}";
            NSAttributeInfo = "<CFKeyedArchiverUID 0x600001a84220 [0x7fff8007d050]>{value = 33}";
            NSAttributes = "<CFKeyedArchiverUID 0x600001a84260 [0x7fff8007d050]>{value = 4}";
            NSDelegate = "<CFKeyedArchiverUID 0x600001a84ee0 [0x7fff8007d050]>{value = 0}";
            NSString = "<CFKeyedArchiverUID 0x600001a86fc0 [0x7fff8007d050]>{value = 2}";
        },
                {
            "$class" = "<CFKeyedArchiverUID 0x600001a843a0 [0x7fff8007d050]>{value = 3}";
            "NS.string" = "Manuscript: Hello World!";
        },
                {
            "$classes" =             (
                NSMutableString,
                NSString,
                NSObject
            );
            "$classname" = NSMutableString;
        },

   

I have verified both the initial saving and saving the changes are occuring on the main thread.

   

FIXED: I wish I knew why, but I cleaned up my debugging code and now the saves work as expected. SIGH. Oh well.

   

Hacking with Swift is sponsored by Sentry

SPONSORED With Sentry’s error and performance monitoring for iOS you see mobile vitals that actually matter, can solve any latency issues quickly, and learn how each release is performing over time.

Get started

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.