CoreStore 在更新数据模型时插入数组作为关系对象

CoreStore insert array as relationship objects while updating data model

我有下一个代码来更新我的锻炼:

func addMuscleGroups(muscleGroups: [MusclEntity], toWorkout: WorkoutEntity, completion: @escaping (ResultInfo<WorkoutEntity>) -> Void) {

        CoreStore.perform(
            asynchronous: { (transaction) -> WorkoutEntity? in
                let workout = transaction.edit(toWorkout)!

                for muscle in muscleGroups {
                    let editedMuscle = transaction.edit(muscle)!
                    workout.muscles?.insert(editedMuscle)
                }

                return workout
        },

            success: { (transactionWorkout) in // here we have muscles for transactionWorkout

                guard let unwrappedTransactionWorkout = transactionWorkout else {
                    return
                }

                let workout = CoreStore.fetchExisting(unwrappedTransactionWorkout) // there are no muscles objects

                guard let unwrappedWorkout = workout else {
                    return
                }

                completion(.data(unwrappedWorkout))
        },
            failure: { (error) in
                completion(.error(StackedError.internalFetchingDataError(message: error.debugDescription)))
        })

    }

但是正如我所见,我在执行异步块时插入了肌肉,但是当我执行 fetchExisting 时,没有在异步块中添加肌肉。

已编辑:

我发现 workout.muscles 中缺少反向关系,现在我添加了它,看起来 CoreStore 正确更新了锻炼实体。

但是如果我多次调用函数,现在我又遇到了另一个问题:

  1. 第一次通话

addMuscleGroups([1, 2, 3, 4, 5]...) 打印(unwrappedWorkout.muscles) [1, 2, 3, 4, 5]

  1. 第二次调用 addMuscleGroups([1, 2, 3, 4, 5, 6, 7]...)

    打印(unwrappedWorkout.muscles) [6, 7]

(为简化示例我使用了 1、2、3、4、5、6、7 数字),但实际上有 MusclEntity 对象

因此,由于第二次调用 addMuscleGroups 函数 unwrappedWorkout.muscles 有两个肌肉对象而不是 7 个。

我添加了短视频:

https://www.screencast.com/t/63cRGKpk0Q

我也在 CoreStore's github 回答了你的问题。

这将是 NSManagedObjects 关于多对多属性的一般行为。

首先,如您所知,反向关系很重要。

其次,Set不推荐用于未排序的@NSManaged属性。建议改用 NSSet。 在那个注释中,对多关系被视为不可变的,这意味着您不能将对象直接插入到 NSSet 值。您可以通过两种方式插入值。

  1. 直接分配一个新的NSSet
var muscles = (workout.muscles as! Set<MusclEntity>?) ?? []
muscles.insert(newMuscle)
workout.muscles = muscles as NSSet
  1. 使用 KVO 可变访问器(为您创建一个 NSMutableSet 代理):
workout.mutableSetValueForKey(#keyPath(WorkoutEntity.muscles)) 
    .addObject(newMuscle)

我有 PlaylistEntity,这就是我导入的样子。当我为我的播放列表执行 transaction.importUniqueObject 时调用此 func,我想在这里做的是导入播放列表和关系的所有需要​​的属性(在这个简单的示例中称为 songs):

public func update(from source: ImportSource, in transaction: BaseDataTransaction) throws {
        self.id = id
        self.title = source["title"] as? String

        if let songsJsonArray = source["songs"] as? [[String: Any]] {
            let songs = try! transaction.importUniqueObjects(
                Into<SongEntity>(),
                sourceArray: songsJsonArray)

            for song in songs {
                self.mutableSetValue(forKey: #keyPath(Playlist.songs))
                    .add(song) // This code will insert new song to NSSet which is unique and can't be duplicated. 100% working code
            }

            // this code:
            // self.songs = NSSet(array: days)
            // actually will not work well for all cases I will explain below
        }
    } 

假设您想将几首歌曲添加到播放列表,并且您调用了此函数,通过将新歌曲添加到播放列表并向服务器发出请求来更新您的本地存储:

inserNewSong(song1, toPlaylistWithId: someId_abc1)
inserNewSong(song2 toPlaylistWithId: someId_abc1)
inserNewSong(song3 toPlaylistWithId: someId_abc1)

所以现在您的本地存储播放列表中有 3 首歌曲。但是我们还在等待服务器的响应。

所以每次在服务器上添加歌曲并且服务器回复时,我都会使用上面的 update func 重新导入播放列表。

服务器的第一个响应将 return 数组中只有一首歌曲,但正如我提到的,我的本地存储已经有 3 首歌曲,但此代码 self.day = NSSet(array: days) 将用一首歌曲替换 3 首歌曲。

第二个响应将 return [song1, song2],此代码 self.day = NSSet(array: days) 将用 2 首歌曲替换 3 首歌曲。

可以肯定的是,如果你有快速连接并且服务器的所有 3 个请求立即完成,你可能不会看到这种效果,尽管我想使用@JohnEstropia 建议的第二种方法:

self.mutableSetValue(forKey: #keyPath(Playlist.songs))
                        .add(song)