Caveat: the answer below is untested, although I used parallel logic in a sample project and that project seems to be working.
There's a couple parts to the answer. As Joakim Danielson says, in order to persist the user's preferred order you will need to save the order in your CoreItem class. The revised class would look like:
public class CoreItem: NSManagedObject, Identifiable{
@NSManaged public var name: String
@NSManaged public var userOrder: Int16
}
The second part is to keep the items sorted based on the userOrder
attribute. On initialization the userOrder
would typically default to zero so it might be useful to also sort by name
within userOrder
. Assuming you want to do this, then in CoreItemList code:
@FetchRequest( entity: CoreItem.entity(),
sortDescriptors:
[
NSSortDescriptor(
keyPath: CoreItem.userOrder,
ascending: true),
NSSortDescriptor(
keyPath:CoreItem.name,
ascending: true )
]
) var coreItems: FetchedResults<CoreItem>
The third part is that you need to tell swiftui to permit the user to revise the order of the list. As you show in your example, this is done with the onMove
modifier. In that modifier you perform the actions needed to re-order the list in the user's preferred sequence. For example, you could call a convenience function called move
so the modifier would read:
.onMove( perform: move )
Your move
function will be passed an IndexSet and an Int. The index set contains all the items in the FetchRequestResult that are to be moved (typically that is just one item). The Int indicates the position to which they should be moved. The logic would be:
private func move( from source: IndexSet, to destination: Int)
{
// Make an array of items from fetched results
var revisedItems: [ CoreItem ] = coreItems.map{ $0 }
// change the order of the items in the array
revisedItems.move(fromOffsets: source, toOffset: destination )
// update the userOrder attribute in revisedItems to
// persist the new order. This is done in reverse order
// to minimize changes to the indices.
for reverseIndex in stride( from: revisedItems.count - 1,
through: 0,
by: -1 )
{
revisedItems[ reverseIndex ].userOrder =
Int16( reverseIndex )
}
}
Technical reminder: the items stored in revisedItems are classes (i.e., by reference), so updating these items will necessarily update the items in the fetched results. The @FetchedResults wrapper will cause your user interface to reflect the new order.
Admittedly, I'm new to SwiftUI. There is likely to be a more elegant solution!
Paul Hudson (Hacking With Swift) has quite a bit more detail. Here is a link for info on moving data in a list. Here is a link for using core data with SwiftUI (it involves deleting items in a list, but is closely analogous to the onMove
logic)