为了实现通过Toggle切换iCloud同步状态,并使用UserDefaults进行状态存储,搜索了不少结果,在实现上均未符合预期(可能是使用姿势不对),将搜索的结果整合在这篇文章中,方便以后查阅。
核心能力:

  1. Core Data 和 iCloud同步整合
  2. Core Data 和 iCloud 存储方式切换
  3. Toggle 和 UserDefaults结合

Apple

Using Core Data With CloudKit

https://developer.apple.com/videos/play/wwdc2019/202

苹果wwdc2019中的session,较详细的讲解了当时开发iCloud数据同步应用开发的流程,但以现在的xcode中已经和当时版本有所不同,可以参考。
同时这个教程下面相关的视频教程均推荐浏览学习,算是官方教程的一部分。

Toggle sync with NSPersistentCloudKitContainer

https://developer.apple.com/forums/thread/118924

在Apple官方论坛中出现的切换iCloud同步状态的问题,这个问题下面记录了几种实现iCloud同步状态切换的方式,主要都是在PersistenceController中描述如何使用NSPersistentCloudKitContainer来实现容器的切换,但是都没有给出从settingView - UserDefaults - Container 之间切换的整体方案。导致在实践中,虽然切换了Container,但是没有解决在设置页面切换的能力。

Stackoverflow

Using Core Data, iCloud and CloudKit for syncing and backup and how it works together

https://stackoverflow.com/questions/24441909/using-core-data-icloud-and-cloudkit-for-syncing-and-backup-and-how-it-works-tog?rq=1

在我本地已经使用Core Data创建了应用,并能正常的增删改查时,想通过iCloudKit将数据进行设备间同步,这个问题中的答案回答了:

  1. CoreData 是本地的数据库
  2. CoreData已经有方案能直接通过iCloud进行同步
  3. 同时iCloudKit和CoreData是相互独立的。

How to check if user is signed in into iCloud before saving data

https://stackoverflow.com/questions/39060513/how-to-check-if-user-is-signed-in-into-icloud-before-saving-data

已经将iCloud同步数据的能力集成到应用后,但是iCloud是需要基于Apple Account等因素才能使用的,所以需要前置校验当前是否已经有账户登录以及是否可用。防止后续在正常使用时,频繁出现异常错误。
但是当前的这个问题中的回答解决不了上述的需求

CKContainer.defaultContainer().accountStatusWithCompletionHandler { accountStatus, error in
        if accountStatus == .NoAccount {
            let alert = UIAlertController(title: "Sign in to iCloud", message: "Sign in to your iCloud account to write records. On the Home screen, launch Settings, tap iCloud, and enter your Apple ID. Turn iCloud Drive on. If you don't have an iCloud account, tap Create a new Apple ID.", preferredStyle: .Alert)
            alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
            self.presentViewController(alert, animated: true, completion: nil)
        } else {
            // Code if user has account here...
        }
    }

SwiftUI toggle switches

https://stackoverflow.com/questions/60506718/swiftui-toggle-switches

这个问题标题感觉不准确,核心是为了解决使用Toggle后,如何实现组件值的切换同步到UserDefaults中,提问者想通过.onTapGesture解决Toggle点击切换时更新UserDefaults的效果,但是未符合预期。
回答中给出了Toggle和.onTapGesture搭配使用的问题,同时给出了一个简单的方案。

CoreData+CloudKit | On/off iCloud sync toggle

https://stackoverflow.com/questions/65355720/coredatacloudkit-on-off-icloud-sync-toggle

这个问题的回答,我做了参考,并实践了一下。
实践时,未重新定义PersistentContainer,但是在setupContainer中集成了这些代码。
其中给我较大启发的是:

  1. 每次切换前需要保存当前上下文中已更新的数据
  2. 每次切换后需要重新拉取数据

Articles

Quickly Sync User Data and Preferences over iCloud, with Swift

https://medium.com/@janakmshah/quickly-sync-user-data-and-preferences-over-icloud-with-swift-4757a3904f1a

如何通过iCloud同步简单的键值对数据

Writing a UserDefaults editor with SwiftUI and property wrappers

https://noahgilmore.com/blog/userdefaults-editor-swiftui/

较完整的例子通过属性包装的方式,解决Toggle和UserDefaults一起使用的问题。

How to use UserDefaults in SwiftUI

https://www.simpleswiftguide.com/how-to-use-userdefaults-in-swiftui/

定义了一个UserSettings对象,并继承ObservableObject,在init方法中从UserDefatuls中获取值,并对UserSettings中字段赋值。
当字段变更时,使用didSet方法,将值设置到UserDefaults中。
也可以是个较优雅的解决方案。

Using CloudKit in SwiftUI

https://schwiftyui.com/swiftui/using-cloudkit-in-swiftui/

这篇文章讲解了配置Core Data使用CloudKit数据同步的方法。较为详细。

Toggling CloudKit sync in a SwiftUI App life cycle

https://www.hackingwithswift.com/forums/swiftui/toggling-cloudkit-sync-in-a-swiftui-app-life-cycle/6071

在SwiftUI的生命周期中切换CloudKit同步的能力,暂未做尝试,从逻辑中区分了local和iCloud两种存储方式,并通过设置将Descriptor中做了更新。
因为没有实践,所以不清楚会有什么弊端。
缺点:只关注了PersistenceController中的切换,没有关注Setting页面