我一直在努力解决一个问题,我非常肯定会让我采用Type-Erasure技术,但我并不是百分百肯定。我已经尝试了几次不同的时间并且感觉很接近,但最终都失败了。我会尽力简化我的问题。假设你有一个实体
struct Expense {
var id: Int?
var amount: Double = 0
}
和等效的Realm对象
class RealmExpense: Object {
let id = RealmOptional<Int>()
let amount = RealmOptional<Double>()
var entity: Expense {
return Expense(id: id.value, amount: amount.value)
}
}
请注意,我可以使用实体变量将RealmExpense转换为Expense。
然后,我有另一个协议
protocol ExpenseRepository: class {
func getAll() -> [Expense]
//...other CRUD methods
}
最后,一个班级
class ExpenseRealmRepository: ExpenseRepository {
private let realm = try! Realm()
func getAll() -> [Expense] {
return realm.objects(RealmExpense.self).flatMap { $0.entity }
}
func insert(item: Expense) {
try! realm.write {
realm.add(RealmExpense(expense: item))
}
}
//...implementation of other CRUD methods
}
现在,这样可以正常工作,但我有很多实体,这是我想重构的重复代码,但是我为使这个泛型做出的每一次尝试都会抛出一个编译器错误或另一个。我想要的是基本上能够创建一个类
class RealmRepository<RepositoryType: Object, EntityType> {
private let realm = try! Realm()
func getAll() -> [EntityType] {
return realm.objects(RepositoryType.self).flatMap { $0.entity }
}
func insert(item: EntityType) {
try! realm.write {
realm.add(RepositoryType(item))
}
}
//...the rest of the CRUD methods.
}
我的主要问题是Swift泛型似乎不允许这种行为。也许我没有探索过更好的整体方法。
David Weston在下面给出的答案是正确的(并且非常好)但是目前有一个Swift错误阻止了Realm Entity初始化程序的使用。参见:
https://github.com/realm/realm-cocoa/pull/2514
基本上,解决方案是使用接受字典的Realm Objects的默认初始化程序。由于我已经使用ObjectMapper库将实体转换为JSON和从JSON转换实体,这就像更改
一样简单func insert(item: T.EntityType) {
realm.write {
realm.add(RealmEntityType(item)) //compiler error when initializing RealmEntityType
}
}
到
func insert(item: T.EntityType) {
let realmItem = RealmEntityType()
realmItem.setValuesForKeys(item.toJSON()) //needs to be a dictionary
try! realm.write {
realm.add(realmItem)
}
}
答案 0 :(得分:4)
这是一个有趣的问题。我不确定您将如何使用这些存储库对象,但根据您的问题到目前为止,我并不需要使用类型擦除。
首先,我们定义了我们需要的通用层次结构:
// implemented by your immutable structs
protocol Entity {
}
// Implemented by RealmSwift.Object subclasses
protocol RealmEntity {
associatedtype EntityType
init(_ entity: EntityType)
var entity: EntityType { get }
}
// protocol to define the operations one can perform on a repository
protocol Repository {
associatedtype EntityType
func getAll() -> [EntityType]
func insert(item: EntityType)
}
// A RealmRepository that implements the Repository protocol on top of Realm
class RealmRepository<T>: Repository where T: RealmEntity, T: Object, T.EntityType: Entity {
typealias RealmEntityType = T
private let realm = Realm()
internal func insert(item: T.EntityType) {
realm.write {
realm.add(RealmEntityType(item))
}
}
internal func getAll() -> [T.EntityType] {
return realm.objects(T.self).flatMap { $0.entity }
}
}
现在我们已经拥有了基础架构,我们可以创建一个ExpenseRepository来显示所有这些东西的编译:
struct Expense: Entity {
var id: Int
var amount: Double
}
class RealmExpense: Object, RealmEntity {
typealias EntityType = Expense
let id: RealmOptional<Int>
let amount: RealmOptional<Double>
required init(_ entity: EntityType) {
id = RealmOptional(entity.id)
amount = RealmOptional(entity.amount)
}
var entity: Expense {
return Expense(id: id.value!, amount: amount.value!)
}
}
var expenseRepository = RealmRepository<RealmExpense>()
这可能无法在您的项目中编译,因为我在一个没有导入Realm的游乐场中创建,但它应该给你一个想法。
现在,如果要存储不同类型的存储库数组或存储库类型的变量,那么当您需要类型擦除时,必须创建AnyRepository
答案 1 :(得分:-1)
我正在尝试编写一个库,您可以在以下位置找到它:
https://github.com/rusito-23/RealmDAO
更新:
基本思想是在名为GenericDAO的类中处理所有Realm事务,该类具有两种关联的类型,一种是从Object扩展的,而另一种是从Transferrable扩展的(通过这种方式,我们可以处理对象的副本,而不是对象本身)。
还有一个额外的协议及其处理自动增量主键的方法!
使用起来应该很简单:
let movieDAO = GenericDAO<Movie>()
movieDAO.findAll(completion: { /*-- YOUR CODE --*/})
您可以在我发送的链接中找到更多信息!
希望有帮助!