Hello, I am stuck with the following problem.
I have a view where i fetch a some data (SetlistsView) and present it
in a List. The add and move functions work flawlessly and the list is
updated as soon as I execute one of these. This is the code for that
view:
struct SetlistsView: View {
@Environment(\.managedObjectContext) private var managedObjectContext
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \Setlist.id, ascending: true)],
animation: .default
)
private var setlists : FetchedResults<Setlist>
var body: some View {
NavigationView {
List {
ForEach(setlists, id: \.self) { setlist in
SetlistRowView(setlist: setlist)
}
.onMove(perform: move)
}
.toolbar {
ToolbarItem {
Button(action: add) {
Label("Add Item", systemImage: "plus")
}
}
}
}
}
func add() {
let newSet = Setlist(context: managedObjectContext)
newSet.id = fetchMaxId() + 1
PersistenceController.shared.save()
}
func move(from oldIndex: IndexSet, to newIndex: Int) {
var revisedItems: [Setlist] = setlists.map{$0}
revisedItems.move(fromOffsets: oldIndex, toOffset: newIndex)
for reverseIndex in stride(from: revisedItems.count-1, through: 0, by: -1) {
revisedItems[reverseIndex].id = Int64(reverseIndex)
}
PersistenceController.shared.save()
}
private func fetchMaxId() -> Int64 {
return (setlists.max(by: { a, b in a.id < b.id}))?.id ?? -1
}
}
For each element in the list I instantiate a SetListRowView to which I pass the setlist element. This is the code for that view:
struct SetlistRowView: View {
// Needs to be observed or edits made in SetlistView
// (like changing name or list order) won't update this view
@ObservedObject var setlist : Setlist
var body: some View {
NavigationLink {
SongsView(setlist: setlist)
} label: {
Text(String(setlist.id))
}
}
}
This acts as a middleman in which I use a NavigationLink to pass the
selected setlist to the SongsView, for which this is the code:
struct SongsView: View {
@Environment(\.managedObjectContext) private var managedObjectContext
@ObservedObject var setlist : Setlist
@State private var songs : [Song]
init(setlist: Setlist) {
self.setlist = setlist
let allObjects = setlist.songs?.allObjects as! [Song]
self.songs = allObjects.sorted(by: {$0.id < $1.id})
}
var body: some View {
List {
ForEach(songs, id: \.self) { song in
Text(String(song.id))
}
.onMove(perform: move)
}
.toolbar {
ToolbarItem {
Button(action: add) {
Label("Add Item", systemImage: "plus")
}
}
}
}
func add() {
let newSong = Song(context: managedObjectContext)
newSong.id = fetchMaxId() + 1
newSong.setlist = setlist
PersistenceController.shared.save()
}
func move(from oldIndex: IndexSet, to newIndex: Int) {
var revisedItems: [Song] = songs.map{$0}
revisedItems.move(fromOffsets: oldIndex, toOffset: newIndex)
for reverseIndex in stride(from: revisedItems.count-1, through: 0, by: -1) {
revisedItems[reverseIndex].id = Int64(reverseIndex)
}
PersistenceController.shared.save()
}
private func fetchMaxId() -> Int64 {
return (songs.max(by: { a, b in a.id < b.id}))?.id ?? -1
}
}
And here's my problem. In the SongsView I the move and add functions
don't work as they should. The add function actually inserts a new item
but the view doesn't get refreshed, I have to get back into the
SetlistsView and enter the SongsView again. The move function doesn't
reassign the id property.
I don't get why these functions work in the SetlistsView but not in the SongsView. I tried changing the songs var as such
@FetchRequest private var songs : FetchedResults<Song>
init(setlist: Setlist) {
self.setlist = setlist
let predicate = NSPredicate(format: "setlist == %@", setlist)
let sortDescriptors = [NSSortDescriptor(keyPath: \Song.id, ascending: true)]
self._songs = FetchRequest(sortDescriptors: sortDescriptors, predicate: predicate)
}
And this seems to work better, as the add function works, but if I
add a name property to the Song object (in the CoreData database) I can
see that, when the move action ends, the songs are put back into their
original order and not actually swapped.
Why the behaviour is different in the two views and how can I fix it? Thank you