SwiftUI Save ManagedObjectContext after Toggle

57 Views Asked by At

In my SwiftUI / iOS 15+ app, I have a toolbar displaying a button which is meant to toggle the "bookmarked" property of an "item" object. The item is passed in as a Binding, as shown here:

import SwiftUI

struct PageViewToolbar: ToolbarContent {
    @Environment(\.managedObjectContext) var context

    @Binding var item: Item //CoreData ManagedObject
    
    var body: some ToolbarContent {
        ToolbarItem(placement: .automatic) {
            Toggle(isOn: self.$item.bookmarked) {
                Image(systemName: "bookmark")
                    .symbolVariant(self.item.bookmarked ? .fill : .none)
            } //Toggle
            .toggleStyle(.button)
        } //ToolbarItem
    } //body
} //PageViewToolbar

The button is toggling and the bookmarked property on the item is being set accordingly, but item is a CoreData ManagedObject and so I need to trigger a context save whenever the property is changed. Is there a way to attach that logic to the Toggle button?

2

There are 2 best solutions below

0
vadian On BEST ANSWER

First of all item is reference type so you must use @ObservedObject rather than @Binding to get notified about changes. Fortunately NSManagedObject conforms to ObservableObject by default.

Then add the .onChange modifier to save the context when bookmarked has changed.

struct PageViewToolbar: ToolbarContent {
    @Environment(\.managedObjectContext) var context

    @ObservedObject var item: Item 
    
    var body: some ToolbarContent {
        ToolbarItem(placement: .automatic) {
            Toggle(isOn: $item.bookmarked) {
                Image(systemName: "bookmark")
                    .symbolVariant(item.bookmarked ? .fill : .none)
            } //Toggle
            .toggleStyle(.button)
            .onChange(of: item.bookmarked) { _ in
                try? context.save()
             }
        } //ToolbarItem
    } //body
} //PageViewToolbar
0
Jinwoo Kim On
import SwiftUI

struct PageViewToolbar: ToolbarContent {
    @Environment(\.managedObjectContext) var context

    @State var bookmarked: Bool
    @Binding var item: Item //CoreData ManagedObject

    init(item: Binding<Item>) {
        _item = item
        bookmarked = item.wrappedValue.bookmarked
    }
    
    var body: some ToolbarContent {
        ToolbarItem(placement: .automatic) {
            Toggle(isOn: $bookmarked) {
                Image(systemName: "bookmark")
                    .symbolVariant(self.bookmarked ? .fill : .none)
            } //Toggle
            .toggleStyle(.button)
        } //ToolbarItem
        .onChange(of: item) { newValue in
            bookmarked = newValue.bookmarked
        }
        .onChange(of: bookmarked) { newValue in
            guard item.bookmarked != newValue else { return }
            item.bookmarked = newValue
            try? context.save()
        }
    } //body
} //PageViewToolbar