Seam3 1.5.8

Seam3 1.5.8

测试已测试
Lang语言 SwiftSwift
许可证 NOASSERTION
发布最后发布2021年2月
SPM支持 SPM

Paul Wilkinson 维护。



Seam3 1.5.8

Seam3

CI Status Version License Platform GitHub stars Carthage compatible

Seam3 是一个用于连接 CoreData 和 CloudKit 之间差距的框架。它几乎处理了所有与 CloudKit 相关的麻烦。您只需要将它用作 CoreData 数据存储的存储类型即可。本地缓存和同步将得到处理。

Seam3 基于 Seamnofelmahmood

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 引用

在上面的表格中: Integer16Integer32Integer64DecimalFloatBoolean 指的是在 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

该策略要求您设置SMStoresyncConflictResolutionBlock属性。您指定的闭包将接收三个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)
}
  • 启用您的应用的推送通知。
  • 在实际AppDelegate中实现didReceiveRemoteNotification方法,并在之前创建的SMStore实例上调用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容器使用您的应用或应用程序的捆绑标识符命名。如果您想要在跨不同平台的应用程序(例如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文件。