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

时间:2019-04-30 12:48:07

标签: ios swift core-data corestore

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

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时,异步块中没有添加肌肉。

已编辑:

正如我发现的那样,execution.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

2 个答案:

答案 0 :(得分:1)

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

这是NSManagedObject关于许多属性的一般行为。

首先,正如您所发现的,逆关系很重要。

第二,不建议将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)

答案 1 :(得分:0)

我有PlaylistEntity,这就是我导入的样子。当我对播放列表执行transaction.importUniqueObject时,将调用此函数,在这里我要做的是导入播放列表和关系的所有必需属性(在这个简单的示例中称为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重新导入播放列表。

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

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

如果我想使用@JohnEstropia建议的第二种方法,请确保如果您具有快速连接并且所有3个请求均立即使服务器完成,则可能看不到这种效果。

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