使用通用函数获取核心数据实体亲属
Get Core Data Entity relatives with a generic function
我正在为我的核心数据模型设计一个数据管理器,我想创建一个通用函数来获取 class 的亲属。
我创建了一个协议,允许为每种数据类型构建管理器。在这个协议中,我已经定义了两个关联类型 T 和 K 以及几个简单的函数。现在我被 class 亲戚获取方法困住了——我需要以某种方式表明 T 有 K 亲戚。我曾尝试创建一些通过相互属性指示这种关系的协议,但没有成功,因此两个 classes 都可以遵守该协议。任何想法,甚至可能吗?
import Foundation
import CoreData
protocol DataManager {
associatedtype T: NSManagedObject, NSFetchRequestResult
associatedtype K: NSManagedObject, NSFetchRequestResult // Relative
static var sharedInstance: Self { get }
static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]?
static func insert(item: T)
static func update(item: T)
static func clean()
static func deleteById(id: String)
// Relatives
static func getRelatives(by: T) -> [K]?
static func get(byRelative: K) -> [T]?
}
extension DataManager {
static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]? {
guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil }
fetchRequest.sortDescriptors = sorted
var results: [T]? = nil
do {
results = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
return results
}
}
protocol Identifiable {
typealias Identity = String
var id: Identity? { get }
}
extension DataManager where Self.T: Identifiable {
static func get(by id: T.Identity, context: NSManagedObjectContext) -> T? {
guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil }
fetchRequest.predicate = NSPredicate(format: "%K == %@", "id", id)
var rawResults: [T]? = nil
do {
rawResults = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
if let result = rawResults?.first {
return result }
else { return nil }
}
}
好吧,我已经创建了一个解决方案。
我们可以识别与特定 class:
的所有关系
let relationships = T.entity().relationships(forDestination: K.entity())
它允许我们为每个关系找到一个项目的所有 ID(对于同一个相对实体我们可以有多个关系):
let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name)
因此,我们可以使用这些 ID 从另一个 class 获取记录。
static func getRelatives(of item: T, context:NSManagedObjectContext) -> [K]? {
guard let fetchRequest: NSFetchRequest<K> = K.fetchRequest() as? NSFetchRequest<K> else { return nil }
fetchRequest.fetchBatchSize = 100
var results: [K]? = nil
var resultSet: Set<K> = [] // doesn't allow duplicates
let relationships = T.entity().relationships(forDestination: K.entity())
for relationship in relationships {
let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name)
let predicate = NSPredicate(format: "self IN %@", relativesIDs)
fetchRequest.predicate = predicate
var batchResults: [K] = []
do {
batchResults = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
if batchResults.count > 0 { resultSet = resultSet.union(Set(batchResults)) }
}
if resultSet.count > 0 { results = Array(resultSet) }
return results
}
我不确定这是不是最优雅的解决方案,但它确实有效:-)
我正在为我的核心数据模型设计一个数据管理器,我想创建一个通用函数来获取 class 的亲属。
我创建了一个协议,允许为每种数据类型构建管理器。在这个协议中,我已经定义了两个关联类型 T 和 K 以及几个简单的函数。现在我被 class 亲戚获取方法困住了——我需要以某种方式表明 T 有 K 亲戚。我曾尝试创建一些通过相互属性指示这种关系的协议,但没有成功,因此两个 classes 都可以遵守该协议。任何想法,甚至可能吗?
import Foundation
import CoreData
protocol DataManager {
associatedtype T: NSManagedObject, NSFetchRequestResult
associatedtype K: NSManagedObject, NSFetchRequestResult // Relative
static var sharedInstance: Self { get }
static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]?
static func insert(item: T)
static func update(item: T)
static func clean()
static func deleteById(id: String)
// Relatives
static func getRelatives(by: T) -> [K]?
static func get(byRelative: K) -> [T]?
}
extension DataManager {
static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]? {
guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil }
fetchRequest.sortDescriptors = sorted
var results: [T]? = nil
do {
results = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
return results
}
}
protocol Identifiable {
typealias Identity = String
var id: Identity? { get }
}
extension DataManager where Self.T: Identifiable {
static func get(by id: T.Identity, context: NSManagedObjectContext) -> T? {
guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil }
fetchRequest.predicate = NSPredicate(format: "%K == %@", "id", id)
var rawResults: [T]? = nil
do {
rawResults = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
if let result = rawResults?.first {
return result }
else { return nil }
}
}
好吧,我已经创建了一个解决方案。 我们可以识别与特定 class:
的所有关系let relationships = T.entity().relationships(forDestination: K.entity())
它允许我们为每个关系找到一个项目的所有 ID(对于同一个相对实体我们可以有多个关系):
let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name)
因此,我们可以使用这些 ID 从另一个 class 获取记录。
static func getRelatives(of item: T, context:NSManagedObjectContext) -> [K]? {
guard let fetchRequest: NSFetchRequest<K> = K.fetchRequest() as? NSFetchRequest<K> else { return nil }
fetchRequest.fetchBatchSize = 100
var results: [K]? = nil
var resultSet: Set<K> = [] // doesn't allow duplicates
let relationships = T.entity().relationships(forDestination: K.entity())
for relationship in relationships {
let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name)
let predicate = NSPredicate(format: "self IN %@", relativesIDs)
fetchRequest.predicate = predicate
var batchResults: [K] = []
do {
batchResults = try context.fetch(fetchRequest)
} catch {
assert(false, error.localizedDescription)
} //TODO: Handle Errors
if batchResults.count > 0 { resultSet = resultSet.union(Set(batchResults)) }
}
if resultSet.count > 0 { results = Array(resultSet) }
return results
}
我不确定这是不是最优雅的解决方案,但它确实有效:-)