☁️ Cirrus

SPM License CI

Cirrus 为 CloudKit 提供简单的同步功能,适用于 Codable Swift 模型。 与其支持所有 CloudKit 功能,Cirrus 更注重简洁性、可靠性和符合 Swift 值类型的工效学设计。

主要特性
🙅 不再需要处理 CKRecordCKOperationCKSubscription
👀 使用 Combine 观察模型和 iCloud 帐户更改
📲 自动 CloudKit 推送通知订阅
🚀 简洁的架构,简洁而强大的 API
🎁 自包含,没有外部依赖项

用法

安装 并按照 Apple 的步骤 在您的 App 中启用 CloudKit

  1. 注册您的应用以接收远程 CloudKit 推送通知
// AppDelegate.swift
func application(
  _ application: UIApplication,
  didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
  ...
  application.registerForRemoteNotifications()
  ...
}
  1. 让您的模型符合 CloudKitCodable
import CloudKitCodable

struct Landmark: CloudKitCodable {
  struct Coordinate: Codable {
    let latitude: Double
    let longitude: Double
  }

  let identifier: UUID
  let name: String
  let coordinate: Coordinate

  // MARK: - CloudKitCodable

  /// A key that uniquely identifies the model. Use this identifier to update your 
  /// associated local models when the sync engine emits changes.
  var cloudKitIdentifier: CloudKitIdentifier {
    return identifier.uuidString
  }

  /// Managed by the sync engine, this should be set to nil when creating a new model.
  /// Be sure to save this when persisting models locally.
  var cloudKitSystemFields: Data? = nil

  /// Describes how to handle conflicts between client and server models.
  public static func resolveConflict(clientModel: Self, serverModel: Self) -> Self? {

    // Use `cloudKitLastModifiedDate` to check when models were last saved to the server
    guard let clientDate = clientModel.cloudKitLastModifiedDate,
      let serverDate = serverModel.cloudKitLastModifiedDate else {
      return clientModel
    }
    return clientDate > serverDate ? clientModel : serverModel
  }
}
  1. 为模型初始化一个 SyncEngine
import Cirrus

let syncEngine = SyncEngine<Landmark>()
  1. 配置 SyncEngine 以处理远程更改
// AppDelegate.swift
func application(
  _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]
) {
  syncEngine.processRemoteChangeNotification(with: userInfo)
  ...
}
  1. 开始同步
// Upload new or updated models
syncEngine.upload(newLandmarks)

// Delete models
syncEngine.delete(oldLandmark)

// Observe remote model changes
syncEngine.modelsChanged
  .sink { change in
    // Update local models
    switch change {
    case let .updated(models):
      ...
    case let .deleted(modelIDs):
      ...
    }
  }

// Observe iCloud account status changes
syncEngine.$accountStatus
  .sink { accountStatus in
    switch accountStatus {
      case .available:
        ...
      case .noAccount:
        ...
      ...
    }
  }

就是这样! Cirrus 也支持同步多种模型类型,只需为您想要同步的每种类型初始化和配置一个新的 SyncEngine

要查看 Cirrus 如何集成到应用程序中的示例,请克隆此存储库并打开 CirrusExample Xcode 项目。

安装

您可以通过将其作为包依赖项添加到 Xcode 项目中来添加 Cirrus。

  1. 文件菜单中,选择Swift Packages › Add Package Dependency…
  2. 在包存储库 URL 文本字段中输入 "https://github.com/jayhickey/cirrus"
  3. 根据您的项目结构
    • 如果您有一个需要访问该库的单一应用程序目标,请将 CirrusCloudKitCodable 直接添加到您的应用程序。
    • 如果您有多个目标,其中您的模型位于一个目标中,但您希望在另一个目标中使用 Cirrus 处理同步,则将 CloudKitCodable 添加到您的模型目标,并将 Cirrus 添加到您的同步目标。

限制

Cirrus 仅支持私有 iCloud 数据库。 如果您需要将数据存储在公共 iCloud 数据库中,那么 Cirrus 不适合您。

CloudKitCodable 模型上的嵌套 Codable 类型不会作为单独的 CKRecord 引用存储; 它们会作为 Data blobs 保存在顶层 CKRecord 上。 这导致了两个重要的注意事项

  1. CKRecord 有一个 1 MB 数据限制,因此大型模型可能不适合单个记录。 SyncEngine 不会尝试同步任何大于 1 MB 的模型。 如果您遇到此限制,请考虑通过创建具有相互标识符引用的离散 CloudKitCodable 模型来规范化您的数据。 您可以使用多个 SyncEngine 来同步每种模型类型。
  2. 如果任何子模型具有引用磁盘文件 URL 的属性,则它们将不会转换为 CKAsset 并存储在 CloudKit 中。 如果您需要存储由子模型上的本地文件 URL 引用的文件,您可以覆盖模型上的 Encodable encode(to:)Decodable init(from:) 方法,以将文件 URL 设置为顶级 CloudKitCodable 类型的编码容器上的键。 然后,SyncEngine 将能够将您的文件同步到 iCloud。

许可证

本库是在 MIT 许可证下发布的。 有关详细信息,请参阅 LICENSE

🙌 特别感谢

感谢 Tim Bueno 帮助构建 Cirrus。