SwiftCoreData 库

GitHub

一个开源库,包含实用程序和扩展,可帮助使用 CoreData

开发为 XII 的 iOS、macOS 和 watchOS 应用程序中的各种项目的可重用组件。

安装

Swift 包管理器

  1. 在 Xcode 中,选择 File > Swift Packages > Add Package Dependency。
  2. 使用此存储库的 URL 按照提示操作
  3. 选择 SwiftCoreData 库以添加到您的项目

依赖项

许可证

请参阅 LICENSE 文件。

定义 CoreData 存储 ()

enum CoreDataStoreConfiguration {
  case CloudSynchedSqliteStore(
    sqliteName: String,
    cloudContainerId: String,
    cloudContainerScope: CKDatabase.Scope,
    configurationName: String?
  )

  case TransientInMemoryStore(
    configurationName: String?
  )

  case LocalSqliteStore(
    sqliteName: String,
    configurationName: String?
  )
}

描述 NSPersistentStore 的配置,如 NSPersistentStoreDescription 所述。

包括对以下内容的支持

提取存储的 URL

extension CoreDataStoreConfiguration {
  var storeUrl: URL { get }
}

根据存储的类型和描述返回存储的完整 URL

创建 NSPersistentContainer ()

extension NSPersistentContainer {
  static func create(
    name: String,
    stores: [CoreDataStoreConfiguration]
  ) async throws -> Self
}

创建一个具有特定名称(与模型文件匹配)的 NSPersistentContainer,以及一组将从其各自的 CoreDataStoreConfiguration 创建的 NSPersistentStore

注意: 使用的存储描述不能具有配置了 iCloud 的存储。


extension NSPersistentContainer {
  static func createMockInMemoryContainer(
    name: String
  ) -> NSPersistentContainer
}

支持在预览中快速初始化:使用给定的名称初始化模拟的内存容器。

NSPersistentCloudKitContainer 的扩展 ()

extension NSPersistentCloudKitContainer {
  func initializeContainerCloudKitSchema() throws
}

在 iCloud 中初始化 NSPersistentCloudKitContainer 的数据库模式。

为了能够初始化云模式

每当模型发生更改时,都应通过从启动时的调试版本调用此函数来初始化云数据库模式。

注意: iCloud 同步对于已初始化模式的会话将不会处于活动状态。

注意: 仅当最初创建公共模式或模式已更改时,才应初始化公共范围存储的云数据库模式。 如果在初始化时由于缺少 CDMR 记录而收到错误,请特别注意。 初始化时创建虚假的多对多关系可以帮助解决这些问题。

注意: 容器内的瞬态存储类型需要在模式初始化期间从容器中排除,否则模式创建将失败。


extension NSPersistentCloudKitContainer {
  func waitForInitialCloudKitImport(
    pollIntervalSeconds: Double,
    pollTimeoutSeconds: Double
  ) async throws
}

等待来自给定容器的事件通知,表明它已完成其初始云套件导入。 以给定的间隔轮询容器事件流,并等待最长请求的超时时间以完成同步。

注意: 导入完成并不意味着数据已从 CloudKit 完全下载,因为用户可能处于离线状态。

NSFetchRequest 创建助手 ()

func createFetchRequest<Target : NSManagedObject>(
  _ type: Target.Type,
  predicate: NSPredicate? = nil,
  sort: [NSSortDescriptor]? = nil,
  offset: Int? = nil,
  limit: Int? = nil
) -> NSFetchRequest<Target>

创建并返回请求的模型类型的获取请求。

获取助手 ()

extension NSManagedObjectContext {
  func fetchSingle<Target : NSManagedObject>(
    _ request: NSFetchRequest<Target>
  ) throws -> Target
}

使用提供的 NSFetchRequest 获取单个记录。

假定请求中的获取限制配置正确,如果读取了多个记录,则会引发错误。

NSPersistentContainer 助手扩展 ()

extension NSPersistentContainer {
  func store(for storeUrl: URL?) throws -> NSPersistentStore
}

返回具有请求的 URL 的存储(如果已提供且已找到),或者当前容器中使用的单个存储(如果没有指定 url 且容器仅包含单个存储)。


extension NSPersistentContainer {
  func newRecord<Target : NSManagedObject>(
    _ type: Target.Type,
    storeUrl: URL?
  ) throws -> Target
}

返回请求的 NSManagedObject 类型的新实例(未填充),连接到 viewContext

(可选)可以指定是否应将记录分配给容器内特定类型的存储(例如,用于瞬态记录的内存存储)。

注意: 由于此操作与容器的 viewContext 交互,因此应在主线程上执行。


extension NSPersistentContainer {
  @discardableResult
  func flushChanges() throws -> Bool
}

将容器的 viewContext 中的任何更改保存到其后备存储。

如果存在要保存的更改且操作成功,则返回 true;如果不存在更改,则返回 false。

注意: 由于此操作与容器的 viewContext 交互,因此应在主线程上执行。


extension NSPersistentContainer {
  func delete<Target : NSManagedObject>(
    _ request: NSFetchRequest<Target>,
    storeUrl: URL?
  ) throws
}

通过 NSBatchDeleteRequest 删除与提供的 NSFetchRequest 匹配的所有记录。

注意: 仅适用于容器的 NSSQLiteStoreType 中的持久性(非瞬态)记录。

注意: 此操作不会自动刷新更改,而是在后备存储上执行删除,然后将其与内存存储同步。

注意: 由于此操作与容器的 viewContext 交互,因此应在主线程上执行。

View 提供 NSManagedObjectContext ()

protocol ManagedObjectContextProvider {
  func viewContext<Target : NSManagedObject>(
    for type: Target.Type
  ) throws -> NSManagedObjectContext
}

定义一个提供程序,该提供程序可以返回正确的 NSManagedObjectContext,用于读取特定类型的 NSManagedObject 记录。

当使用存储属性包装器(@StorageRecords@StorageRecord@StorageOptionalRecord)并且应用程序具有多个 NSManagedObjectContext 时,这是必需的。


extension View {
  func managedObjectContextProvider(
    _ provider: ManagedObjectContextProvider
  ) -> some View
}

将特定的 ManagedObjectContextProvider 注册到 Environment

用法示例

class CoreDataService : ManagedObjectContextProvider {
  ...

  func viewContext<Target : NSManagedObject>(
    for type: Target.Type
  ) throws -> NSManagedObjectContext {
    ...
  }
}

struct FooView : View {
  @StateObject private var coreDataService = CoreDataService()

  var body : some View {
    ContentView()
      .managedObjectContextProvider(coreDataService)
  }
}

SwiftUI 获取属性包装器

加载多个记录 ()

@propertyWrapper
struct StorageRecords<Target : NSManagedObject> : DynamicProperty {
  init(_ fetchRequest: NSFetchRequest<Target>)

  var wrappedValue: [Target] { get }

  nonmutating func update()
}

提供对 Target 类型的 NSFetchRequest 结果的访问。

类似于 @FetchRequest,但通过视图环境中可用的 ManagedObjectContextProvider 处理获取。 这允许支持应用程序的多个上下文,并将读取操作路由到特定的上下文实例。

示例

struct FooView : View {
  @StorageRecords(
    fetchRequest: ...
  ) private var records: [MyManagedObjectType]

  var body : View {
    Text("Loaded: \(records.count)")
  }
}

加载单个记录 ()

@propertyWrapper
public struct StorageRecord<Target : NSManagedObject> : DynamicProperty {
  init(_ fetchRequest: NSFetchRequest<Target>)

  var wrappedValue: Target { get }

  nonmutating func update()

  @dynamicMemberLookup
  public struct Wrapper {
    subscript<Value>(
      dynamicMember keyPath: ReferenceWritableKeyPath<Target, Value>
    ) -> Binding<Value>
  }

  var projectedValue: StorageRecord<Target>.Wrapper
}

提供对 Target 类型的 NSFetchRequest 结果的访问。

结果假定为单个托管记录。 如果找到多个记录或零个记录,则会引发错误。

类似于 @FetchRequest,但通过视图环境中可用的 ManagedObjectContextProvider 处理获取。 这允许支持应用程序的多个上下文,并将读取操作路由到特定的上下文实例。

支持特定记录字段的扩展绑定。

示例

struct FooView : View {
  @StorageRecord(
    fetchRequest: ...
  ) private var record: MyManagedObjectType

  var body : View {
    Text("Loaded: \(record.myField)")

    SomeSubView(binding: $record.myField)
  }
}

加载可能不存在的记录 ()

@propertyWrapper
public struct StorageOptionalRecord<Target : NSManagedObject> : DynamicProperty {
  init(_ fetchRequest: NSFetchRequest<Target>)

  var wrappedValue: Target? { get }

  nonmutating func update()
}

提供对 Target 类型的 NSFetchRequest 结果的访问。

结果假定为单个托管记录。 如果找到多个记录,则会引发错误。 如果找到零个记录,则结果将为 nil。

类似于 @FetchRequest,但通过视图环境中可用的 ManagedObjectContextProvider 处理获取。 这允许支持应用程序的多个上下文,并将读取操作路由到特定的上下文实例。

示例

struct FooView : View {
  @StorageOptionalRecord(
    fetchRequest: ...
  ) private var record: MyManagedObjectType?

  var body : View {
    Text("Loaded: \(record.myField ?? "N/A")")
  }
}

NSManagedObject 扩展 ()

extension NSManagedObject {
  func get<Target>(_ value: Optional<Target>) -> Target

  func get(_ value: NSNumber?) -> Int?

  func set(_ value: Int?) -> NSNumber?

  func get(_ value: NSNumber?) -> Int

  func set(_ value: Int) -> NSNumber?

  func get(_ value: NSNumber?) -> Double

  func set(_ value: Double) -> NSNumber?

  func get<Target : RawRepresentable>(
    _ value: String?
  ) -> Target where Target.RawValue == String

  func set<Source : RawRepresentable>(
    _ value: Source
  ) -> String? where Source.RawValue == String

  func get<Target : RawRepresentable>(
    _ value: String?
  ) -> Target? where Target.RawValue == String

  func set<Source : RawRepresentable>(
    _ value: Source?
  ) -> String? where Source.RawValue == String

  func get(_ value: String?) -> TimeZone

  func set(_ value: TimeZone) -> String

  func get<Target : NSManagedObject>(_ value: NSSet?) -> [Target]

  func set<Source : NSManagedObject>(_ value: [Source]) -> NSSet?

  func get(_ value: String?) -> URL

  func set(_ value: URL) -> String
}

为希望公开其属性的类型的模型提供数据转换简写,该类型与其存储的类型属性类型不同(例如,枚举值与存储的 String)。

示例

extension SomeNSManagedObjectType {
  var aliasedAttribute: String {
    get { get(storedAttribute) }
    set { storedAttribute = set(newValue) }
  }
}