The question is best explained in an example:
struct MyEditor: View {
@Environment(\.managedObjectContext) var managedObjectContext
@ObservedObject var song: Song
var body: some View {
TextEditor(text: $song.lyrics)
.navigationTitle(song.title)
.onChange(of: song.lyrics) { newValue in
try? managedObjectContext.save()
}
}
}
It feels wrong to spam save
but I want to make sure the data is stored. Is this allowed and a correct way to do it?
Another way I can think of is to create a publisher that smoothens the signal to save. If this is the correct way to do it. Can I retrieve the publisher from the ObservedObject or do I have to create a different @State
property for that and use onChange
to pass the values.
struct MyEditor: View {
@Environment(\.managedObjectContext) var managedObjectContext
@ObservedObject var song: Song
@State private var lyricsPublisher = PassthroughSubject<String, Never>()
var body: some View {
TextEditor(text: $song.lyrics)
.navigationTitle(song.title)
.onChange(of: song.lyrics) { newValue in
lyricsPublisher.send(newValue)
}
.onReceive(lyricsPublisher
.debounce(for: 0.5, scheduler: RunLoop.main)
.removeDuplicates()
) { value in
try? managedObjectContext.save()
}
}
}
This is what Song looks like as a ManagedObject
.
@objc(Song)
class Song: NSManagedObject, Identifiable {
@nonobjc class func fetchRequest() -> NSFetchRequest<Song> {
return NSFetchRequest<Scribble>(entityName: "Song")
}
@NSManaged public var id: UUID
@NSManaged public var title: String
@NSManaged public var lyrics: String
}
Yes, it is possible to use publisher of managed object (ObservedObject
) directly, like
var body: some View {
TextEditor(text: $song.lyrics)
.navigationTitle(song.title)
.onReceive(song.publisher(for: \.lyrics) // << here !!
.debounce(for: 0.5, scheduler: DispatchQueue.main)
.removeDuplicates()
) { value in
try? managedObjectContext.save()
}
}