Seam3 是一个框架,用于弥合 CoreData 和 CloudKit 之间的差距。它几乎处理了所有 CloudKit 的麻烦。你所要做的就是将其用作 CoreData 存储的存储类型。本地缓存和同步都已处理好。
Seam3 基于 Seam,作者是 nofelmahmood
Seam3 的变更包括
CoreData | CloudKit |
---|---|
NSDate | Date/Time (日期/时间) |
Binary Data (二进制数据) | Bytes 或 CKAsset (见下文) |
NSString | String (字符串) |
Integer16 | Int(64) |
Integer32 | Int(64) |
Integer64 | Int(64) |
Decimal (十进制) | Double (双精度浮点数) |
Float (单精度浮点数) | Double (双精度浮点数) |
Boolean (布尔值) | Int(64) |
NSManagedObject | Reference (引用) |
在上表中:Integer16
、Integer32
、Integer64
、Decimal
、Float
和 Boolean
是指用于在 CoreData 模型中表示它们的 NSNumber
的实例。NSManagedObject
指的是 CoreData 模型中的 `一对一关系`。
如果一个 Binary Data (二进制数据)
属性选择了允许外部存储选项,它将被存储为 CloudKit 中的 CKAsset
,否则它将被存储为 Bytes (字节)
在 CKRecord
本身中。
CoreData 关系 | CloudKit 上的转换 |
---|---|
一对一 | 一对一关系在 CloudKit 服务器上被转换为 CKReferences。 |
一对多 | 不会显式创建一对多关系。Seam3 仅在 CloudKit 服务器上创建和管理一对一关系。 示例 -> 如果一个 Employee (员工) 有一个到 Department (部门) 的一对一关系,并且 Department (部门) 有一个到 Employee (员工) 的一对多关系,那么 Seam3 只会在 CloudKit 服务器上创建前者。它将通过使用一对一关系来实现后者。如果访问一个部门的所有员工,Seam3 将通过获取属于该特定部门的所有员工来实现。 |
注意:你必须在应用程序的 CoreData 模型中创建反向关系,否则 Seam3 将无法将 CoreData 模型转换为 CloudKit 记录。可能会发生意外错误和数据损坏。
Seam3 使 CoreData 存储与 CloudKit 服务器保持同步。它通过抛出以下两个通知来让你知道同步操作何时开始和结束。
smSyncDidStart
Notification (通知)
smSyncDidFinish
Notification (通知)
如果在同步操作期间发生错误,则 smSyncDidFinish
通知的 userInfo
属性将包含一个键为 SMStore.SMStoreErrorDomain
的 Error (错误)
对象
如果发生任何同步冲突,Seam3 会公开 3 种冲突解决策略。
clientTellsWhichWins (客户端决定胜者)
此策略要求你设置 SMStore
的 syncConflictResolutionBlock
属性。你指定的闭包将接收三个 CKRecord
参数;第一个是当前的服务器记录。第二个是当前的客户端记录,第三个是最近更改之前的客户端记录。你的闭包必须修改并返回作为第一个参数传递的服务器记录。
serverRecordWins (服务器记录胜出)
这是默认值。它将服务器记录视为真实记录。
clientRecordWins (客户端记录胜出)
这会将客户端记录视为真实记录。
var smStore: SMStore?
SMStore.type
的存储类型添加到应用程序的 NSPersistentStoreCoordinator,并将其分配给在上一步中创建的属性。SMStore.registerStoreClass()
do
{
self.smStore = try coordinator.addPersistentStoreWithType(SMStore.type, configuration: nil, URL: url, options: nil) as? SMStore
}
NSPersistentContainer
的 iOS10lazy var persistentContainer: NSPersistentContainer = {
SMStore.registerStoreClass()
let container = NSPersistentContainer(name: "Seam3Demo2")
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
if let applicationDocumentsDirectory = urls.last {
let url = applicationDocumentsDirectory.appendingPathComponent("SingleViewCoreData.sqlite")
let storeDescription = NSPersistentStoreDescription(url: url)
storeDescription.type = SMStore.type
container.persistentStoreDescriptions=[storeDescription]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}
fatalError("Unable to access documents directory")
}()
默认情况下,日志将被写入 os_log
,但你可以通过扩展 SMLogger
将日志消息路由到你自己的类。
class AppDelegate: SMLogDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
SMStore.logger = self
}
// MARK: SMLogDelegate
func log(_ message: @autoclosure() -> String, type: SMLogType) {
#if DEBUG
switch type {
case .debug:
print("Debug: \(message())")
case .info:
print("Info: \(message())")
case .error:
print("Error: \(message())")
case .fault:
print("Fault: \(message())")
case .defaultType:
print("Default: \(message())")
}
#endif
}
}
你可以使用以下方式访问 SMStore
实例
self.smStore = container.persistentStoreCoordinator.persistentStores.first as? SMStore
在触发同步之前,你应该检查 Cloud Kit 身份验证状态并检查是否有已更改的 Cloud Kit 用户
self.smStore?.verifyCloudKitConnectionAndUser() { (status, user, error) in
guard status == .available, error == nil else {
NSLog("Unable to verify CloudKit Connection \(error)")
return
}
guard let currentUser = user else {
NSLog("No current CloudKit user")
return
}
var completeSync = false
let previousUser = UserDefaults.standard.string(forKey: "CloudKitUser")
if previousUser != currentUser {
do {
print("New user")
try self.smStore?.resetBackingStore()
completeSync = true
} catch {
NSLog("Error resetting backing store - \(error.localizedDescription)")
return
}
}
UserDefaults.standard.set(currentUser, forKey:"CloudKitUser")
self.smStore?.triggerSync(complete: completeSync)
}
handlePush
。func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
print("Received push")
smStore?.handlePush(userInfo: userInfo) { (result) in
completionHandler(result.uiBackgroundFetchResult)
}
}
默认的 Cloud Kit 容器是使用你的应用程序或应用的bundle identifier来命名的。如果你想在不同平台上的应用程序(例如 iOS 和 macOS)之间共享 Cloud Kit 数据,那么你需要使用命名的 Cloud Kit 容器。你可以在创建 SMStore 实例时指定一个 cloud kit 容器。
在 iOS10 上,使用 NSPersistentStoreDescription
对象指定 SMStore.SMStoreContainerOption
let storeDescription = NSPersistentStoreDescription(url: url)
storeDescription.type = SMStore.type
storeDescription.setOption("iCloud.org.cocoapods.demo.Seam3-Example" as NSString, forKey: SMStore.SMStoreContainerOption)
在 iOS9 和 macOS 上,为持久性存储协调器指定一个选项字典
let options:[String:Any] = [SMStore.SMStoreContainerOption:"iCloud.org.cocoapods.demo.Seam3-Example"]
self.smStore = try coordinator!.addPersistentStore(ofType: SMStore.type, configurationName: nil, at: url, options: options) as? SMStore
确保你在 Xcode 中应用程序的capabilities (功能)选项卡的iCloud containers (iCloud 容器)下选择了你指定的值。
迁移应该非常简单,因为用于在 CloudKit 和本地后备存储中存储数据的格式没有改变。将 import 语句更改为 import Seam3
,你就可以开始了。
要运行示例项目,请克隆 repo,并首先从 Example 目录运行 pod install
。如果你在模拟器上运行,请确保使用模拟器中的设置应用登录到 iCloud。
Seam3 可通过 CocoaPods 获得。要安装它,只需将以下行添加到你的 Podfile
pod "Seam3"
paulw, paulw@wilko.me
Seam3 在 MIT 许可下可用。有关更多信息,请参见 LICENSE 文件。