How can I implement onMove in a List of CoreData FetchedResults
Asked Answered
F

2

8

I have a List showing CoreData FetchedResults. I would like to implement the possibility to move row and at the same time update the order attribute of the entity. FetchedResults is not an array so I cannot use the move property of Array. This is how I've implemented this but is not working very well.

func move(fromOffsets: IndexSet, toOffset: Int) {
        var orders: [Int16] = Array(1...Int16(myEntities.count))
        orders.move(fromOffsets: fromOffsets, toOffset: toOffset)
        for (entity, order) in zip(myEntities, orders) {
            entity.order = order
        }
    }

In my code I get an array of the current order, I perform the move and then I reassign them.

I think the best option would be to create a custom move property for Collection where Element: MyEntity, Index == Int.

Any idea?

To recreate scenario you can easily start new SwiftUI Master-Detail project with CoreData option selected, then just add the order attribute to the entity (Remember to sort the @FetchRequest with NSSortDescriptor(keyPath: \MyEntity.order, ascending: true)])

Frissell answered 28/3, 2020 at 15:40 Comment(2)
no better idea, do it very similar!Cherubini
This just helped me after hours! Thank you! Similar code wasn't working for me at all - sorting the order array then zipping with the object array seems to be the fix. Still a small issue with the UI needing to be refreshed but almost there! :)Striker
P
1

I find it just needs some tweaking:

///example: source = 1, destination = 4
///original list: [a,b,c,d,e]
///final list: [b,c,d,a,e]
func move(from source: IndexSet, to destination: Int)
         var sortedOrders: [Int16] = Array(1...Int16(myEntities.count))
        for (entity, order) in zip(myEntities, sortedOrders) {
            entity.order = order
        }
        
        sortedOrders.move(fromOffsets: source, toOffset: destination)
        var finalOrders : [Int16] = Array(1...Int16(myEntities.count))
        var mappingDict = Dictionary(uniqueKeysWithValues: zip(sortedOrders , finalOrders ) )
        // [5: 5, 4: 3, 2: 1, 1: 4, 3: 2]
        
        for entity in myEntities{
            entity.order = mappingDict[entity.order]!
        }

}
Penal answered 7/11, 2021 at 5:31 Comment(0)
M
1

Here's my spin on @luomein's answer, which provides a generic way to get the indices map, making the implementation of move functions much simpler.

So first we add this extension to Collection:

extension Collection {
    func newIndices(moving source: IndexSet, to destination: Int) -> [Int: Int] {
        var oldIndexesByNewIndex: [Int] = Array(0..<self.count)
        oldIndexesByNewIndex.move(fromOffsets: source, toOffset: destination)
        return Dictionary(uniqueKeysWithValues:
                            oldIndexesByNewIndex.enumerated().map{ newIndex, oldIndex in (oldIndex, newIndex) }
        )
    }
    
}

Then we use it like this:

    func move(from source: IndexSet, to destination: Int) {
        let newIndices = entities.newIndices(moving: source, to: destination)
        entities.enumerated().forEach { currentIndex, entity in
            if (newIndices[currentIndex] != currentIndex) {
                entity.order = newIndices[currentIndex]!
            }
        }
    }
Mahayana answered 24/3, 2023 at 0:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.