CorePersistence
CorePersistence 是一个 pod,为 您处理所有 CoreData 的管理。
特性
- 简单集成
- 开箱即用的 CoreData 解决方案
- 在 sqlite 中线程安全的 CRUD 操作
- 无冲突操作
- 解析时的自动类型转换
- 易于使用的谓词
使用方法
CorePersistence 是一个易于使用的 CocoaPod,用作 CoreData 的包装器和简单的开箱即用的 JSON 解析器。所有写入操作都是在后台上下文中完成的,并且通过 OperationsQueue 进行同步。实体唯一性通过定义一个 keypath 来确保,这个 keypath 指向一个属性,该属性将用作唯一 id 约束。这些实体可以通过实现 Parsable 协议来自动解析,并且通过使用 <- 运算符自动执行所有转换、关系链接。
集成
在使用 CorePersistence 的功能之前,需要首先通过调用以下方法进行初始化:
func initializeStack(with modelName: String, handleMigration: (() -> Void)? = nil)它需要你的 .xcdatamodeld 名称,以及一个闭包,在发生重型转换时触发。**目前,在重型迁移的情况下,当前 .sqlite 文件将被删除,并重新创建为新模型版本**。
持久化
当创建目标为持久化的模型时,你必须实现 Persistable 协议。以下是实现此协议的一个示例实体:
User+CoreDataClass.swift
@objc(User)
final public class User: ParsableManagedObject {
public static var idKeyPath: WritableKeyPath<User, String> {
return \Self.uuid
}
}User+CoreDataProperties.swift
extension User {
@nonobjc public class func fetchRequest() -> NSFetchRequest<User> {
return NSFetchRequest<User>(entityName: "User")
}
@NSManaged public var uuid: String
@NSManaged public var firstName: String?
@NSManaged public var lastName: String?
}CRUD
创建
static func create(in store: StoreManager,
updateIfEntityExists: Bool,
updateClosure: @escaping (_ entity: Self, _ context: NSManagedObjectContext) -> Void,
completeClosure: ((Self) -> Void)?)在 NSPersistentStore 的后台上下文中创建一个 Persistable 实体,使用 store 参数。如果触发 updateClosure,则在一个后台线路上传递一个新创建的对象,或者如果对象已存在,则传递要更新的对象。
参数
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence中定义的一个。 - updateIfEntityExists: 当在数据库中找到重复项时,该方法将执行的动作由该标志定义。如果设置为
true,则获取现有的一个并再调用一次updateClosure来更新现有实体,如果设置为false,则将省略更新。 - updateClosure: 当创建新实体或检索现有实体时触发。
- entity: 需要更新的新创建的实体。
- context: 创作用于此上下文。可用于创建关系实体。
- completeClosure: 在保存后台上下文后,异步在主线程上分发完整的块,同时从主上下文重新检索一个新鲜的对象。
示例
User.create(updateClosure: { (userToUpdate, context) in
userToUpdate.uuid = "b4c7900b-10f0-45ff-8692-0ff1e5ce2ac4"
userToUpdate.firstName = "Mark"
userToUpdate.lastName = "Twain"
userToUpdate.birthDate = Date()
userToUpdate.address = Address(entityID: "a8b6c763-eb10-4e82-b872-5504ee4c762c", context: context)
}, completeClosure: { persistedUser in
print(persistedUser)
})init(entityID: EntityID, in store: StoreManager, context: NSManagedObjectContext)独占用于初始化关系实体。应在调用 create 或 update 时使用,并且已有上下文可用。
参数
- entityID: 每个实体都必须有的唯一 ID,且只能有一个实体有这个特定的一个。
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence`中定义的一个。 - context: 执行创建的上下文。
示例
User.create(updateClosure: { (userToUpdate, context) in
userToUpdate.uuid = "b4c7900b-10f0-45ff-8692-0ff1e5ce2ac4"
userToUpdate.address = Address(entityID: "a8b6c763-eb10-4e82-b872-5504ee4c762c", context: context)
}, completeClosure: { persistedUser in
print(persistedUser)
})
user.update(updateClosure: { (userToUpdate, context) in
userToUpdate.address = Address(entityID: "a8b6c763-eb10-4e82-b872-5504ee4c762c", context: context)
}, completeClosure: { updatedUser in
print(updatedUser)
}static func createTemporary(in store: StoreManager,
updateClosure: @escaping (Self, NSManagedObjectContext) -> Void)创建一个临时对象,该对象将在 updateClosure 闭包执行之后销毁。
参数
- 存储: 包含
NSPersistentStore的StoreManager实例。默认参数是 StoreManagers 的default,其中包含默认的NSPersistentStore。 - updateClosure: 新临时对象用于编辑的闭包。
- entity: 要更新的新创建的实体。
- context: 在上执行创建的上下文。可用于创建关系实体。
示例
User.createTemporary { (temporaryUser, context) in
temporaryUser.uuid = "b4c7900b-10f0-45ff-8692-0ff1e5ce2ac4"
temporaryUser.firstName = "Mark"
temporaryUser.lastName = "Twain"
}读
static func get(entityID: EntityID,
from store: StoreManager,
sourceContext: NSManagedObjectContext) -> Self?从使用 store 参数定义的 StoreManager 实例中检索单个实体,默认从 store 的主上下文中,但可以通过 sourceContext 参数传入不同的上下文。
参数
- entityID: 每个实体都必须有的唯一 ID,且只能有一个实体有这个特定的一个。
- store: 包含
NSPersistentStore的StoreManager实例。默认是在Persistence中定义的。 - sourceContext: 在其中查找
entityID的NSManagedObjectContext实例。默认是mainContext。 - 返回: 带有
entityID的现有对象,如果找不到则返回 nil。
示例
let user = User.get(entityID: "8fda9bf4-4631-4290-ac4b-7ce62a3aacd6")static func get(from store: StoreManager,
using predicate: NSPredicate,
comparisonClauses: [ComparisonClause],
sourceContext: NSManagedObjectContext) -> [Self]从使用 store 参数定义的 StoreManager 实例中检索多个实体,默认从 store 的主上下文中,但可以通过 sourceContext 参数传入不同的上下文。
参数
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence中定义的一个。 - predicate: 用于检索实体的查询谓词。
- comparisonClauses:
ComparisonClause实例数组。 - sourceContext: 在其中查找
entityID的NSManagedObjectContext实例。默认是mainContext。 - 返回:来自
sourceContext的新对象,符合predicate条件,并按comparisonClauses排序
示例
let usersWithNameMark = User.get(using: \User.firstName == "Mark" && \User.birthDate <= Date(),
comparisonClauses: [.ascending(\User.birthDate)])static func getAll(from store: StoreManager,
comparisonClauses: [ComparisonClause],
sourceContext: NSManagedObjectContext) -> [Self]检索所有由 store 参数定义的 StoreManager 实例中的实体,默认从主上下文的 store 检索,但也可以通过 sourceContext 参数传递不同的上下文。
参数
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence中定义的一个。 - comparisonClauses:
ComparisonClause实例数组。 - sourceContext: 在其中查找
entityID的NSManagedObjectContext实例。默认是mainContext。 - 返回:所有现有对象
示例
let allUsers = User.getAll(comparisonClauses: [.descending(\User.birthDate)])更新
func update(in store: StoreManager,
updateClosure: @escaping (_ entity: Self, _ context: NSManagedObjectContext) -> Void,
completeClosure: ((Self) -> Void)?)在更新闭包中更新对象。
参数
- store: 包含
NSPersistentStore的StoreManager实例。默认是在Persistence中定义的。 - updateClosure:用于编辑对象的闭包
- entity: 要更新的新创建的实体。
- context: 创作用于此上下文。可用于创建关系实体。
- completeClosure:在主线程上保存对象的闭包
示例
user.update(updateClosure: { (user, context) in
user.firstName = "New Name"
let newOrder = Order(entityID: "b8b3183d-2ea9-477d-99c5-98f7e7707ef4", context: context)
user.orders.insert(newOrder)
}, completeClosure: { updatedUser in
print(updatedUser)
})删除
func delete(from store: StoreManager,
sourceContext: NSManagedObjectContext,
completeClosure: (() -> Void)?)从指定 context 参数的 NSManagedObjectContext 中删除实体。
参数
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence中定义的一个。 - 上下文:源
NSManagedObjectContext。默认为主上下文 - 完成闭包:在上下文保存后触发的事件
示例
user.delete {
print("Entity deleted")
}static func delete(from store: StoreManager,
with options: DeleteOptions,
completeClosure: (() -> Void)?)按照在 DeleteOptions 中定义的 predicate 条件条件删除实体结果集合。
参数
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence中定义的一个。 - 选项:一个
DeleteOptions实例,可以包含NSPredicate、ComparisonClause实例、offset和执行上下文。 - 完成闭包:在上下文保存后触发的事件
示例
User.delete(with: DeleteOptions(predicate: \User.birthDate < Date())) {
print("Finished deleting")
}解析
要启用模型解析,它们必须实现 Parsable 协议。自动符合 Persistable 协议,所以如果实体应该被持久化和解析,只需要实现 Parsable 协议。
有一个需要实现的方法
func mapValues(from map: MappingValues)在从 parse(...) 的调用中调用的方法。在这个方法中,可以使用自定义运算符 <- 定义从 MappingValues 的解析。**警告:此方法不应手动调用或从主线程调用**。也不应手动设置 id,即 id = 3,因为 parse(...) 方法在调用此方法之前从 JSON 字典中获取 id,这可能会破坏 CoreData 中主键的唯一性。
参数
- map:包裹
[String: Any]字典的包装器,执行字典的基本操作,且对NSManagedObjectContext有感知。
示例
final class Entity: ParsableManagedObject {
public func mapValues(from map: MappingValues) {
title <- map["title"]
date <- (map["date"], { anyDate in
return Date.transform(anyDate, dateFormats: ["yyyy-MM-dd"])
})
}运算符 <- 用于将 MappingValues 对象的值转换并设置到实体的属性中。它从 Any 自动转换为 Transformable 类型。
可转换类型
- Int, Int16, Int32
- Double
- Date
- Optional
- 前面类型的数组
- 前面类型的字典
- 符合
Transformable协议的枚举
特性
- 自动将不兼容的基本类型(Int、Int16、Int32、Double、String)转换为合适的属性类型。
- 如果尝试将实体id解析为任何类型的关系,将创建适当的实体并将其设置为关系或添加到
Set - 如果传递数组或字典进行解析到一个单个实体关系时,它将解析为单个实体;如果它是
一对一或一对多关系,将创建一个常规的Set - 不会为字典中没有相应键设置的属性设置值。
在JSON中定义主键
在调用 mapValues 之前,主键会被自动解析。该键默认设置为通过 idKeyPath 计算得到的字符串值。这意味着在 JSON 中,主键的值将自动解析为与之同名的键所代表的值。如果这两个键不同,即 JSON 中的键为 id 而属性名为 uuid,则必须实现一个计算属性 jsonKey,以便解析方法知道哪个键包含主键。
示例
{
"user_id": 123456
}
final class Entity: ParsableManagedObject {
@NSManaged var id: Int
public static var idKeyPath: WritableKeyPath<User, Int> {
return \Self.id
}
// Here we need to implement `jsonKey` because key `user_id` and variable `id` which holds primary key differ
public static var jsonKey: String {
return "user_id"
}
}
final class Entity: ParsableManagedObject {
@NSManaged var user_id: Int
public static var idKeyPath: WritableKeyPath<User, Int> {
return \Self.user_id
}
// In this case it's not needed because they have the same name
//public static var jsonKey: String {
// return "user_id"
//}
}
方法
static func parse(json: JSONObject, in store: StoreManager, completeClosure: ((Self?) -> Void)?)用于对 Parsable 类型进行解析的方法。给定一个 json 字典,实体使用 mapValues(:) 进行解析,然后在后台上下文中持久化到 NSPersistentStore。如果存储中已存在相同对象,将对其进行更新,以保持 entityID 的唯一性。
参数
- json:要解析的数据的字典。[String: Any] 类型。
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence中定义的一个。 - completeClosure:解析完成后,使用来自
store的主上下文中的新实体触发completeClosure。
示例
let json: [String: Any] = [
"uuid": "a8b6c763-eb10-4e82-b872-5504ee4c762c",
"date": "2000-04-11"
]
User.parse(json: json) { parsedEntity in
print(parsedEntity)
}static func parse(jsonArray: [JSONObject], in store: StoreManager, completeClosure: (([Self]) -> Void)?)用于对 Parsable 类型进行解析的方法。给定一个包含字典的 json 数组,实体使用 mapValues(:) 进行解析,然后在后台上下文中持久化到 NSPersistentStore。如果存储中存在任何对象,则这些对象将进行更新,以保持 entityID 的唯一性。
参数
- jsonArray:应解析的数据包含的 [String: Any] 字典的数组。
- store: 包含
NSPersistentStore的StoreManager实例。默认为在CorePersistence中定义的一个。 - completeClosure:解析完成后,使用一个由存储库主上下文中的实体组成的新数组触发
completeClosure。
示例
let jsonArray: [[String: Any]] = [
["uuid": "a8b6c763-eb10-4e82-b872-5504ee4c762c",
"date": "2000-04-11"],
["uuid": "649e5991-c591-4055-8649-d1e6264ee768",
"date": "1996-02-08"]
]
User.parse(jsonArray: jsonArray) { parsedEntities in
print(parsedEntity)
}断言
NSPredicate 使用中存在一个问题,那就是没有自动完成或编译时检查。在 CorePersistence 中,有一些工具操作符可以将键路径和条件创建到 NSPredicates 中。
let userID = a8b6c763-eb10-4e82-b872-5504ee4c762c
// Example 1:
NSPredicate(format: "userID == %@ AND numberOfOrders != 0", userID)
// is is the same as:
\User.uuid == userID && \User.numberOfOrders != 0
// Example 2:
NSPredicate(format: "numberOfOrders >= 0")
// is the same as
\User.numberOfOrders >= 0
let uuids = ["669cdb66-58ba-4579-a951-1b0acac7aae5", "b8b3183d-2ea9-477d-99c5-98f7e7707ef4"]
// Example 3:
NSPredicate(format: "uuid IN %@", uuids)
// is the same as
\User.uuid === uuids比较从句
ComparisonClause 被代替 NSSortDescriptor 以使用键路径而非字符串。有两种静态变量可以使用。
public static func ascending<EntityType: PersistableManagedObject, PropertyType>(_ keyPath: KeyPath<EntityType, PropertyType>) -> ComparisonClause
public static func descending<EntityType: PersistableManagedObject, PropertyType>(_ keyPath: KeyPath<EntityType, PropertyType>) -> ComparisonClause示例
results = Results<User>()
.filterBy(\User.address != nil)
.sortBy(.ascending(\User.birthDate), .descending(\User.address))结果
结果对象是围绕 NSFetchedResultsController 的一种包装,封装了其功能,并改为使用闭包机制在上下文中传递更新,而不是多个委托方法以及大量模板代码。主要变化是刷新闭包会在所有更改都发生时触发,而不是逐个触发。
示例
results = Results<User>()
.filterBy(\User.address != nil)
.sortBy(.ascending(\User.birthDate), .descending(\User.address))
.registerForChanges { (changes) in
print("Changes: \(changes.map { ($0.type, $0.object.uuid) })")
}如果结果定义在当前作用域内,并且在外部作用域内没有保留引用,则不会发生刷新,因为对象将被回收。
方法
public init(context: NSManagedObjectContext = StoreManager.default.mainContext)初始化器,用于获取类型为 EntityType 的所有实体,并按 idKeyPath 排序。
参数
- context: 来源上下文
示例
results = Results<User>()public func filterBy(_ predicate: NSPredicate) -> Self过滤之前获取的结果。
参数
- predicate: 用于定义过滤规则的谓词
示例
results = Results<User>().filterBy(\User.birthDate <= Date())public func sortBy(_ comparisons: ComparisonClause...) -> Self对之前获取的结果进行排序。
参数
- 比较:可以在
ascending或descending中选择的一类ComparisonClause实例
示例
results = Results<User>().sortBy(.ascending(\User.date()), .descending(\User.lastName))public func registerForChanges(closure: @escaping (_ changes: [ResultsRefresher<EntityType>.Change]) -> Void) -> Self{用于注册变更,这些变更在 Results 的初始化中指定的 NSManagedObjectContext 上发生。当发生变更时,将触发 closure 并传递累积的变更。变更包含一个变更 type(插入、更新、移动、删除),在插入和移动类型的情况下可能不为 nil 的 newIndexPath,当更新、移动和删除时将不为 nil 的 indexPath,以及 EntityType 类型的对象。**注意**: 如果 Results 实例在其创建的范围内没有保存,则 ResultsRefresher 闭包将与 Results 一起解除分配,并且不会发生任何变更。
参数
- 闭包:当上下文发生变更时将被触发
- 变更:类型为
Change的数组,定义在ResultsRefresher中,包含变更type(插入、更新、移动、删除),在插入和移动类型的情况下可能不为 nil 的newIndexPath,在更新、移动和删除时将不为 nil 的indexPath,以及EntityType类型的对象。
示例
results = Results<User>()
.registerForChanges { (changes) in
print("Changes: \(changes.map { ($0.type, $0.object.uuid) })")
}public func toArray() -> [EntityType]将结果对象转换为类型为 EntityType 的数组
示例
let entityArray = Results<User>()
.filterBy(\User.address != nil)
.sortBy(.ascending(\User.birthDate), .descending(\User.address))
.registerForChanges { (changes) in
print("Changes: \(changes.map { ($0.type, $0.object.uuid) })")
}.toArray()日志记录
存在几种实用日志记录方法,可以帮助记录不同严重性的事件。
🔵🔵🔵 Verbose log 🔵🔵🔵
public static func verbose(_ message: String)
🟠🟠🟠 Warning log 🟠🟠🟠
public static func warning(_ message: String)
🔴🔴🔴 Error log 🔴🔴🔴
public static func error(_ message: String)示例
要运行示例项目,首先克隆仓库,然后从示例目录运行 pod install 命令。
要求
- iOS 10.0+
- Xcode 8.3+
- Swift 5+
安装
CorePersistence 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中。
pod 'CorePersistence'作者
Milos Babic,[email#5728;prot;ected
许可
CorePersistence 在 MIT 许可下提供。有关更多信息,请参阅 LICENSE 文件。