Seam3
Seam3 是一个用于连接 CoreData 和 CloudKit 之间差距的框架。它几乎处理了所有与 CloudKit 相关的麻烦。您只需要将它用作 CoreData 数据存储的存储类型即可。本地缓存和同步将得到处理。
Seam3 基于 Seam 由 nofelmahmood
Seam3 的变更包括
- 修正 CoreData 和 CloudKit 之间一对一和多对一关系映射的问题
- 添加 CoreData 中二进制属性和 CloudKit 中的 CKAssets 之间的映射
- 为 Swift 3.0 在 iOS 10、Mac OS 10.11 & tvOS 10 上进行代码更新
- 重构代码以消除全局变量的使用
CoreData 到 CloudKit
属性
CoreData | CloudKit |
---|---|
NSDate | 日期/时间 |
二进制数据 | 字节或 CKAsset(见下文) |
NSString | 字符串 |
Integer16 | Int(64) |
Integer32 | Int(64) |
Integer64 | Int(64) |
Decimal | Double |
Float | Double |
Boolean | Int(64) |
NSManagedObject | 引用 |
在上面的表格中: Integer16
、Integer32
、Integer64
、Decimal
、Float
和 Boolean
指的是在 CoreData 模型中用来表示它们的 NSNumber
实例。《NSManagedObject》 指的是 CoreData 模型中的一对一关系。
如果对二进制数据
属性选择了允许外部存储选项,则将在Cloud Kit中将其存储为CKAsset
,否则将作为Bytes
存储在CKRecord
本身中。
关系
CoreData 关系 | 在CloudKit上的映射 |
---|---|
一 | 一与其关系在CloudKit服务器上映射为CKReferences。 |
多 | 多与其关系不会显式创建。Seam3仅在CloudKit服务器上创建和管理一对一关系。 示例 -> 如果员工与部门有一对一关系,而部门与员工有多对多关系,那么Seam3仅将在CloudKit服务器上创建前者。它将通过使用一对一关系来满足后者。如果访问部门的全部员工,Seam3将通过网络获取属于该特定部门的全部员工。 |
注意:必须在应用的CoreData模型中创建反向关系,否则Seam3无法将CoreData模型映射到CloudKit记录。可能会发生意外错误和数据损坏。
同步
Seam3将CoreData存储与CloudKit服务器保持同步。它将抛出以下两个通知来通知您同步操作的开始和结束。
smSyncDidStart
通知
smSyncDidFinish
通知
如果在同步操作期间发生错误,则smSyncDidFinish
通知的用户信息(userInfo)属性将包含一个名为SMStore.SMStoreErrorDomain
的键的Error
对象。
冲突解决策略
在发生任何同步冲突的情况下,Seam3会公开3个冲突解决策略。
clientTellsWhichWins
该策略要求您设置SMStore
的syncConflictResolutionBlock
属性。您指定的闭包将接收三个CKRecord
参数;第一个是当前服务器记录。第二个是当前客户端记录,第三个是最近一次更改之前的客户端记录。您的闭包必须修改并返回作为第一个参数传递的服务器记录。
serverRecordWins
这是默认设置。它将服务器记录视为真实记录。
clientRecordWins
这被视为真正的记录。
如何使用
- 在存储CoreData的类中声明一个SMStore类型属性。
var smStore: SMStore?
- 对于iOS9和更早以及macOS,将
SMStore.type
的存储类型添加到您的应用程序的NSPersistentStoreCoordinator中,并将其分配给上一步中创建的属性。
SMStore.registerStoreClass()
do
{
self.smStore = try coordinator.addPersistentStoreWithType(SMStore.type, configuration: nil, URL: url, options: nil) as? SMStore
}
- 对于iOS10使用
NSPersistentContainer
lazy 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)
}
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容器使用您的应用或应用程序的捆绑标识符命名。如果您想要在跨不同平台的应用程序(例如iOS和macOS)之间共享Cloud Kit数据,则需要使用命名的Cloud Kit容器。您可以在创建SMStore实例时指定云套件容器。
在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应用程序的“能力”标签下的“iCloud容器”中已选择。
从Seam迁移到Seam3
迁移应该是相当直接的,因为CloudKit和本地备份存储中存储数据的格式没有变化。更改导入语句为import Seam3
,然后您就可以继续使用了。
示例
要运行示例项目,请克隆仓库,然后从示例目录中运行pod install
。如果您正在使用模拟器,请确保使用模拟器中的设置应用登录iCloud。
安装
Seam3可通过CocoaPods获取。要安装它,只需将以下行添加到您的Podfile中
pod "Seam3"
作者
paulw, [email protected]
许可证
Seam3遵循MIT许可证。有关更多信息,请参阅LICENSE文件。