【问题标题】:CoreStore insert array as relationship objects while updating data modelCoreStore 在更新数据模型时插入数组作为关系对象
【发布时间】:2019-04-30 12:48:06
【问题描述】:

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

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

已编辑:

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

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

  1. 第一次通话

addMuscleGroups([1, 2, 3, 4, 5]...) 打印(展开的锻炼。肌肉) [1、2、3、4、5]

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

    打印(展开的锻炼.肌肉) [6, 7]

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

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

我添加了短视频:

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

【问题讨论】:

  • 当你的锻炼.muscles 关系被声明为可选时......你是否在插入之前检查它是否不为零(editedMuscle)
  • 谢谢!是的,它不是零,我看到如果我们在 xcdatamodeld 中对于某些关系没有反转,那么 CoreStore 希望它正确映射。例如,在我的情况下,workout.muscles 关系与workout 没有相反的关系。所以我通过添加逆来解决这个问题,现在它可以工作了。但我有另一个问题我更新了我的问题
  • 插入前是否需要检查是否存在?如您所见,我使用了 Swift Set 而不是 NSSet,我想它可以完全覆盖
  • 我在这里添加了一个调用函数的视频(我在上面指定了):screencast.com/t/63cRGKpk0Q

标签: ios swift core-data corestore


【解决方案1】:

我也在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)

【讨论】:

  • 我将 Swift.Set 更改为 @NSManaged 属性的 NSSet,现在您的代码对我有用,谢谢!
  • 我注意到有些人建议使用这个:“默认情况下,泛型,多对多关系将生成为 NSSet?但是现在谁可以处理无类型容器?使用本机 Swift改为设置类型:”从此链接raizlabs.com/dev/2016/04/core-data-type-safety-with-swift 无论如何我切换到 NSSet 并且一切正常。
  • 很久以前 Xcode 实际上为无序关系生成了 Set&lt;T&gt;,但他们发现它破坏了 Swift 桥接,正如您所观察到的,由于 Core Data 使用的具体 NSSet 子类的动态行为。所以他们将其恢复为NSSet。我忘记了 Apple 在哪里记录了这种行为,但我认为它在过去的 Xcode 发行说明之一中
  • 约翰我已经发布了答案,希望有一天这对某人有所帮助。只是想让你看看。所以我用你已经回答了哪个完美,但是这个workout.muscles = muscles as NSSet 在某些情况下非常棘手,我在答案中突出显示了它们。一如既往地谢谢你!
【解决方案2】:

我有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) 将用 2 首歌曲替换 3 首歌曲。

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

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

【讨论】:

    猜你喜欢
    • 2019-09-14
    • 1970-01-01
    • 2020-12-17
    • 1970-01-01
    • 2020-04-27
    • 2019-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多