Y—Persistence
一个 Core Data 封装器,利用泛型的强大功能,使您可以处理自定义模型对象。

许可

Y—Persistence 基于 Apache 2.0 许可证 发布。

文档

文档自动从源代码注释生成,并呈现为通过 GitHub Pages 托管的静态网站:https://yml-org.github.io/ypersistence-ios/

用法

YPersistence

PersistenceManager 用作 Core Data 的 NSPersistentContainer 的封装器,并且还提供托管对象上下文以执行核心数据操作。

您需要为每个 NSPersistentContainer 实例化一个 PersistenceManager。 这应该在应用程序启动时完成。 然后,您应该使用依赖注入将其传递给需要它的类。

标准初始化器允许您指定模型名称、合并策略和捆绑包。 它为合并策略和捆绑包提供了合理的默认值,但您需要提供模型名称。

首次使用之前,您需要在持久性管理器上调用 load,这是一个异步操作。 通常它很快,但在需要迁移时可能需要几秒钟。

import YPersistence

final class AppCoordinator {
    let persistenceManager = PersistenceManager(modelName: "MyModel")
    
    func configure(completion: @escaping () -> Void) {
        // configure analytics, network, etc.
        ...
        
        // configure persistence
        persistenceManager.load { _ in
            completion()
        }
    }
}

PersistenceManager

每个持久性管理器都有三种方法来提供托管对象上下文

mainContext 返回主上下文,仅适用于主线程上的只读操作。

workerContext 返回一个新的私有队列上下文,适用于短期的添加、编辑或删除操作。

contextForThread 返回一个适用于仅在当前线程上进行只读操作的上下文。 从主线程调用时,它将返回 mainContext。 从后台线程调用时,它将为该线程创建一个新的私有队列上下文(如果尚未存在)并缓存它。

在大多数简单的用例中,您甚至不需要担心管理上下文,因为对于保存和删除操作,将创建一个本地 workerContext,对于提取操作,将使用适当的 contextForThread。 对于高级用例,我们支持传入您希望使用的上下文。

重要提示: 所有写入 Core Data 容器的操作都应通过短期的 worker 上下文完成。 读取可以从任何上下文完成,只要它与创建上下文的线程相同。

协议

Y—Persistence 利用泛型的强大功能,使您可以执行常见的操作(例如提取、保存和删除),而无需为每个实体(SQL 表)创建单独的查询。 它还允许您在 Core Data NSManagedObject 和通用模型对象(结构体或类)之间进行转换。 为了使此工作正常进行,模型对象需要符合不同的协议。

// a business object
struct Person {
    let personId: String
    let name: String
}

// a managed object
class PersonRecord: NSManagedObject {
    @NSManaged var id: String!
    @NSManaged var name: String!
}

CoreModel

CoreModel 协议用于表示任何唯一可识别的模型对象。 它要求模型对象具有唯一标识符,但该标识符可以是任何适当的类型(StringIntUUID 是常见类型)。 本质上,Y—Persistence 中使用的所有模型对象(无论是 JSON 模型对象还是 Core Data NSManagedObject)都需要符合 CoreModel

extension Person: CoreModel {
    typealias UidType = String
    public var uid: String { personId }
}

extension PersonRecord: CoreModel {
    typealias UidType = String
    public var uid: String { id ?? "" }
}

DataRecord

DataRecord 协议用于表示任何 Core Data 记录。 它扩展了 CoreModel,因此我们的记录需要是唯一可识别的(以便我们可以提取、删除或保存)。 它需要

extension PersonRecord: DataRecord {
    static var entityName: String { "PersonRecord" }
    static var uidKey: String { "id" }    
}

ModelRepresentable

ModelRepresentable 协议用于表示可以与模型对象(可以是符合 CoreModel 的任何结构体或类)关联的任何 Core Data 记录。 这将用于帮助在 Core Data 记录和业务模型对象之间进行转换。

extension PersonRecord: ModelRepresentable {
    typealias ModelType = Person
}

RecordFromModel

RecordFromModel 协议用于表示可以从关联模型对象填充的任何 Core Data 记录。 常见用例:从 API 调用返回的模型对象将记录保存到 Core Data。

extension PersonRecord: RecordFromModel {
    func fromModel(_ model: Person) {
        id = model.personId
        name = model.name
    }
}

RecordToModel

RecordToModel 协议用于表示可用于填充关联模型对象的任何 Core Data 记录。 常见用例:从 Core Data 中提取记录作为模型对象,这些模型对象可以在 API POST 请求中使用(或作为线程安全的模型对象移交给 UI)。

extension PersonRecord: RecordToModel {
    func toModel() -> Person {
        Person(
            personId: id ?? "",
            name: name ?? ""
        )
    }
}

常用操作

符合上述某些协议允许 Y—Persistence 执行通用操作,例如提取、保存和删除,而无需为 Core Data 模型中的每个不同实体(SQL 表)构建唯一查询。

提取

可以通过单个 uid 或 uid 数组完成提取,并且可以返回记录(NSManagedObject 子类)或模型。

func fetchPerson(uid: String) throws -> Person? {
    try persistenceManager.fetchModel(entity: PersonRecord.self, uid: uid)
}

func fetchPeople(uids: [String]) throws -> [Person] {
    try persistenceManager.fetchModel(entity: PersonRecord.self, uids: uids)
}

删除

可以通过传递 uid 或传递单个或多个模型对象(从中提取 uid)来执行删除。

func deletePeople(by uids: [String]) throws {
    try persistenceManager.deleteRecords(entity: PersonRecord.self, uids: uids)
}

func delete(person: Person) throws {
    try persistenceManager.deleteModel(entity: PersonRecord.self, model: person)
}

func delete(people: [Person]) throws {
    try persistenceManager.deleteModels(entity: PersonRecord.self, models: people)
}

保存

可以对模型对象数组执行保存,并且可以选择覆盖现有记录。 当用远程模型替换本地记录时使用 shouldOverwrite: true,或者当您缓存分页提取的结果时使用 false

func save(people: [Person]) throws {
    try persistenceManager.save(
        entity: PersonRecord.self, 
        models: people,
        shouldOverwrite: true
    )
}

安装

您可以通过将其添加为包依赖项来将 Y—Persistence 添加到 Xcode 项目。

  1. 文件菜单中,选择添加包...
  2. 在包存储库 URL 文本字段中输入“https://github.com/yml-org/ypersistence-ios
  3. 单击添加包

为 Y—Persistence 做出贡献

要求

SwiftLint (linter)

brew install swiftlint

Jazzy(文档)

sudo gem install jazzy

设置

克隆 repo 并在 Xcode 中打开 Package.swift

版本控制策略

我们使用 语义版本控制

{major}.{minor}.{patch}

例如

1.0.5

分支策略

我们为我们的框架采用简化的分支策略。

分支命名约定

feature/{ticket-number}-{short-description}
bugfix/{ticket-number}-{short-description}

例如

feature/CM-44-button
bugfix/CM-236-textview-color

拉取请求

在提交拉取请求之前,您应该

  1. 编译并确保没有警告和错误。
  2. 运行所有单元测试并确认一切通过。
  3. 检查单元测试覆盖率并确认所有新的/修改的代码都已完全覆盖。
  4. 从命令行运行 swiftlint 并确认没有违规行为。
  5. 从命令行运行 jazzy 并确认您具有 100% 的文档覆盖率。
  6. 考虑使用 git rebase -i HEAD~{commit-count} 将您最后的 {commit-count} 个提交合并到功能块中。
  7. 如果自您创建分支以来,父分支(通常为 main)的 HEAD 已更新,请使用 git rebase main 变基您的分支。
    • 永远不要将父分支合并到您的分支中。
    • 始终从父分支变基您的分支。

提交拉取请求时

合并拉取请求时

发布新版本

生成文档 (通过 Jazzy)

您可以使用以下终端命令直接从源代码生成自己的本地文档集

jazzy

这会在 /docs 下生成一组文档。 默认配置在默认配置文件 .jazzy.yaml 文件中设置。

要查看其他文档选项,请键入

jazzy --help

每次将提交推送到 main 时,GitHub Action 都会自动运行,该 Action 运行 Jazzy 以生成我们 GitHub 页面上的文档:https://yml-org.github.io/ypersistence-ios/