fatbobman / blogComments

1 stars 0 forks source link

Core Data with CloudKit(二) —— 同步本地数据库到 iCloud 私有数据库 | 肘子的Swift记事本 #113

Open fatbobman opened 2 years ago

fatbobman commented 2 years ago

https://www.fatbobman.com/posts/coreDataWithCloudKit-2/

本篇文章中,我们将探讨 Core Data with CloudKit 应用中最常见的场景——将本地数据库同步到 iCloud 私有数据库。

akring commented 2 years ago

读完这篇 CloudKit 相关的博文,受益匪浅,有两个小问题想请教:

  1. CloudKit 不支持 Unique constraints,那么在实践中,保证数据唯一性的最佳实践应该是什么呢?

  2. 经过测试,勾选了 ordered 的 Relationship 也同样不受 CloudKit 支持,这个有没有其他的替代方案呢?

测试用的项目为 SwiftUI + CoreData,后期尝试加入 CloudKit 同步支持

fatbobman commented 2 years ago

你好, Unique constraints 是依赖 SQLilte 的内置能力实现的。其SQL代码类似下面的形式: CREATE UNIQUE INDEX Z_Movie_UNIQUE_color_colors ON ZMOVIE (ZCOLOR COLLATE BINARY ASC, ZCOLORS COLLATE BINARY ASC) 目前只能在保存的时候通过自行编写的代码进行判断。同 Unique constraints一样,在代码中判断指定的属性(或多个属性)是否已经有相同的记录。

对于ordered,如确有需要,可以为托管对象创建计算属性,将对多关系的数据按照自行设定的排序顺序进行返回。相较于使用预置的对多关系返回结果进行排序,自行创建的计算属性性能更好,可控性也更高。

2022年1月19日 下午1:36,Chris Akring @.***> 写道:

读完这篇 CloudKit 相关的博文,受益匪浅,有两个小问题想请教:

CloudKit 不支持 Unique constraints,那么在实践中,保证数据唯一性的最佳实践应该是什么呢?

经过测试,勾选了 ordered 的 Relationship 也同样不受 CloudKit 支持,这个有没有其他的替代方案呢?

测试用的项目为 SwiftUI + CoreData,后期尝试加入 CloudKit 同步支持

— Reply to this email directly, view it on GitHub https://github.com/fatbobman/blogComments/issues/113#issuecomment-1016101995, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANIYIGN7EV3FSZ2GQ2H4PLLUWZEVFANCNFSM5F37YEIA. You are receiving this because you authored the thread.

akring commented 2 years ago

好的,感谢答疑🙏

tailang commented 2 years ago

肘子哥您好,请教个问题,我这有这样一个需求:如果用户不是会员的时候,不同步Core Data到Cloud,如果后来该用户开通了会员,需要将Core Data同步到Cloud,我是否可以通过设置cloudDesc.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "your.cloudKit.container")来控制

fatbobman commented 2 years ago

肘子哥您好,请教个问题,我这有这样一个需求:如果用户不是会员的时候,不同步Core Data到Cloud,如果后来该用户开通了会员,需要将Core Data同步到Cloud,我是否可以通过设置cloudDesc.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "your.cloudKit.container")来控制

你好,可以的。cloudDesc.cloudKitContainerOptions 为 nil 时 NSPersistentCloudKitContainer 其实就是个 NSPersistentContainer 。在启动时检查用于是否为会员,从而实现不同的设置可以实现你的需求。不过,如果你只使用一个 container 的话,app 需要在冷启动的情况下,配置才会起作用。也就是说,用户开了会员,只有在下次 app 彻底重启后,同步功能才会被启用。 实时切换也是可以的,但会麻烦不少。首先要在 stack 中读取 momd 文件创建 model,在 stack 中定义两个 Container ( 一个本地、一个云 ),都使用 lazy 加载的方式。创建一个动态变量 container ,根据是否开会员返回对应的 container 实例。

tailang commented 2 years ago

@fatbobman

肘子哥您好,请教个问题,我这有这样一个需求:如果用户不是会员的时候,不同步Core Data到Cloud,如果后来该用户开通了会员,需要将Core Data同步到Cloud,我是否可以通过设置cloudDesc.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "your.cloudKit.container")来控制

你好,可以的。cloudDesc.cloudKitContainerOptions 为 nil 时 NSPersistentCloudKitContainer 其实就是个 NSPersistentContainer 。在启动时检查用于是否为会员,从而实现不同的设置可以实现你的需求。不过,如果你只使用一个 container 的话,app 需要在冷启动的情况下,配置才会起作用。也就是说,用户开了会员,只有在下次 app 彻底重启后,同步功能才会被启用。 实时切换也是可以的,但会麻烦不少。首先要在 stack 中读取 momd 文件创建 model,在 stack 中定义两个 Container ( 一个本地、一个云 ),都使用 lazy 加载的方式。创建一个动态变量 container ,根据是否开会员返回对应的 container 实例。

感谢肘子哥答疑,阅读了这个系列的文章受益匪浅。

gaowei-china commented 1 year ago

老哥,真机测试时添加数据再删除app重新跑可以还原iCloud的数据,发布到App Store后安装新增数据然后删除app重新安装却不能还原iCloud数据呢?请问是哪里少设置了吗?

fatbobman commented 1 year ago

老哥,真机测试时添加数据再删除app重新跑可以还原iCloud的数据,发布到App Store后安装新增数据然后删除app重新安装却不能还原iCloud数据呢?请问是哪里少设置了吗?

开发环境中的数据与发行环境中的数据是无法互通的。 另外,你还要检查一下,是否在 cloudKit 控制台中,对设定更改进行了发布。否则在发布环境下( App Store、testFlight )是无法实现网络同步的。

image
kingslay commented 1 year ago

你好。想要根据Attribute来判断这个数据是否要同步到iCloud,我看你有提供了一个方案是用两个container: 但是对于你最后一步的处理看的不是很懂,是否可以更详细的指导下,或是有文章链接吗?在网站找了很久,没有找到把viewContext 的数据同步到viewContext的方式。难道是要把数据查询出来,然后复制一个model,在插入到数据库里吗? 谢谢 处理NSPersistentStoreRemoteChange通知,将从localContainer中写入的数据合并到container的viewContext中。

fatbobman commented 1 year ago

你好。想要根据Attribute来判断这个数据是否要同步到iCloud,我看你有提供了一个方案是用两个container: 但是对于你最后一步的处理看的不是很懂,是否可以更详细的指导下,或是有文章链接吗?在网站找了很久,没有找到把viewContext 的数据同步到viewContext的方式。难道是要把数据查询出来,然后复制一个model,在插入到数据库里吗? 谢谢 处理NSPersistentStoreRemoteChange通知,将从localContainer中写入的数据合并到container的viewContext中。

如果想对同一个 Entity 的数据进行有选择性的同步,也可以只使用一个 container。 在模型编辑器中创建两个 Configuration ,其中分别包含了该 Entity 在 Stack 中,loadDescription 之前,只给其中一个设置 CloudKitOption,并分别设置对应的 URL 之后,在代码中 fetch 该 Entity 数据时,Core Data 会自动将两个不同的数据库中的 Entity 数据合并,并统一返回给你 在保存数据时,你可以使用托管上下文的 assign 方法,为其设置对应的持久化存储,这样就可以在代码中决定哪个数据要同步,哪个不同步 https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext/1506436-assign 因为要依靠 NSPersistentStore 来决定是否同步,因此,在 Stack 中,loadDescription 后,最好保存一下同步和非同步的 Store,方便之后的保存方法使用。

kingslay commented 1 year ago

非常感谢你的回复,我有试过这个方法,但是因为我的数据是一开始保存到本地,只有当满足一定的条件了,才想要推送到iCloud。所以我用这个方法目前报错Can't reassign an object to a different store once it has been saved. 看了网上的解决方案是要先把数据复制一份,然后插入到有配置同步的数据库,然后从原有的数据库里把这个数据给删了。但是这样太麻烦了。

fatbobman commented 1 year ago

需要删除原来的数据,在另一个 store 中创建新的。其实封装成一个方法,并不麻烦。

I-NOCoder commented 9 months ago

你好,请问,笔记中的图片数据也要使用 core data 进行同步吗?开启 Allows External Storage 有没有影响?谢谢。

fatbobman commented 9 months ago

@I-NOCoder 如果数据库需要同步,且图片数据保存在数据库内,那么在同步的过程中,图片数据也会一并进行同步的。开启 allow external storage 不受影响,可以正常同步

I-NOCoder commented 9 months ago

@fatbobman @I-NOCoder 如果数据库需要同步,且图片数据保存在数据库内,那么在同步的过程中,图片数据也会一并进行同步的。开启 allow external storage 不受影响,可以正常同步

谢谢,如果图片数据多的话,对 core data 影响大不大?

fatbobman commented 9 months ago

@I-NOCoder 启用外部存储的话,不会影响 Core Data 的效率。但是数量量大的话,必然会影响同步的时间。

I-NOCoder commented 9 months ago

@fatbobman @I-NOCoder 启用外部存储的话,不会影响 Core Data 的效率。但是数量量大的话,必然会影响同步的时间。

好的,感谢

spencerfeng commented 8 months ago

你好,如果我把录音文件保存在文件系统,在core data里保存文件的路径,怎样做才能保证同时录音文件和保存在core data的路径都能使用iCloud同步呢?

fatbobman commented 8 months ago

@spencerfeng 如果文件容量较大,可以考虑与 iCloud Documents 结合使用。将文件保存在共享目录中。在另一台设备上,获取到 url 后,需要首先检查当前的状态,如果为占位符,选择下载。如果容量较小,还是推荐保存在 Core Data 中,可以开启保存在外部的选项。这样就无需自己处理同步了。 https://fatbobman.com/zh/posts/in-depth-guide-to-icloud-documents/

spencerfeng commented 8 months ago

非常感谢🙏

wdlgame commented 6 months ago

“官方文档中这个限制我比较困惑,因为即使不采用网络同步,开发者也通常不会为两个 Configuration 中的实体建立 relationship。如果需要建立联系,通常会采用创建 Fetched Properties”

这里有问题请教,当一些数据为公共数据库,另外一些通过Private数据库进行同步时,此时就无法自然的建立 relationship了。而采用创建 Fetched Properties 是要如何做呢?本来有关系的两个数据,现在因为一个在公共数据库,一个在私有数据库而无法建立relationship。

fatbobman commented 6 months ago

Fetched Properties 其实就相当于预设了谓词的形式进行获取。 建议你在这种情况下,为两边的数据分别设置可以用作检索的谓词属性。数据编辑器中的 Fetched Properties 灵活性比较差 分别在不同的数据库中进行检索。 比如,一边有一个属性 uid , 那么就在另一边也保存对应的 uid ,通过这个 uid 进行检索。

ybwdaisy commented 4 months ago

在keyboard extension中通过viewContext.fetch拿到的不是main app中最新的数据,如何解决?

fatbobman commented 4 months ago

@ybwdaisy 的 keyboard extension 没有了解。不过照理来说,应该在 extension 或 widget 中,使用持久化历史跟踪来保证数据的一致性。 https://fatbobman.com/zh/posts/persistent-history-tracking-in-swiftdata/

ybwdaisy commented 4 months ago

@fatbobman @ybwdaisy 的 keyboard extension 没有了解。不过照理来说,应该在 extension 或 widget 中,使用持久化历史跟踪来保证数据的一致性。 https://fatbobman.com/zh/posts/persistent-history-tracking-in-swiftdata/

感谢,我先试试 https://github.com/fatbobman/PersistentHistoryTrackingKit