在 Swift 中创建通用领域存储库

Gui*_*rez 3 generics realm swift

我一直在努力解决一个问题,我很确定这会让我采用类型擦除技术,但我不是 100% 确定。我尝试了几次不同的时间,感觉很接近,但最终都失败了。我会尽量简化我的问题。假设你有一个实体

struct Expense {
    var id: Int?
    var amount: Double = 0
}
Run Code Online (Sandbox Code Playgroud)

和一个等效的领域对象

class RealmExpense: Object {
    let id = RealmOptional<Int>()
    let amount = RealmOptional<Double>()

    var entity: Expense {
        return Expense(id: id.value, amount: amount.value)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我可以使用实体变量将 RealmExpense 转换为 Expense。

然后,我有另一个协议

protocol ExpenseRepository: class {
    func getAll() -> [Expense]
    //...other CRUD methods
}
Run Code Online (Sandbox Code Playgroud)

最后,一堂课

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
}
Run Code Online (Sandbox Code Playgroud)

现在,这工作正常,但我有很多实体,这是我想重构的重复代码,但我为使这个通用程序所做的每一次尝试都会抛出一个或另一个编译器错误。我想要的是基本上能够创建一个类

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.
}
Run Code Online (Sandbox Code Playgroud)

我的主要问题是 Swift 泛型似乎不允许这种类型的行为。也许有更好的整体方法我还没有探索过。

注意:以下是 Dave Weston 回答的小附录:

下面 David Weston 给出的答案是正确的(并且非常好),但目前存在一个 Swift 错误,该错误阻止了 Realm Entity 初始值设定项的使用。看:

https://github.com/realm/realm-cocoa/pull/2514

基本上,解决方案是使用接受字典的领域对象的默认初始值设定项。由于我已经在使用 ObjectMapper 库将实体与 JSON 相互转换,这就像更改一样简单

func insert(item: T.EntityType) {
    realm.write {
        realm.add(RealmEntityType(item))   //compiler error when initializing RealmEntityType
    }
}
Run Code Online (Sandbox Code Playgroud)

func insert(item: T.EntityType) {
    let realmItem = RealmEntityType()         
    realmItem.setValuesForKeys(item.toJSON())  //needs to be a dictionary
    try! realm.write {
        realm.add(realmItem)
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ton 8

这是一个有趣的问题。我不确定您将如何使用这些存储库对象,但根据您目前的问题,我不需要使用类型擦除。

首先,我们定义我们需要的通用层次结构:

// 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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们有了基础设施,我们可以创建一个 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>()
Run Code Online (Sandbox Code Playgroud)

这可能无法在您的项目中编译,因为我是在没有导入 Realm 的操场中创建的,但它应该给您一个想法。

现在,如果您想存储不同类型的存储库数组,或存储库类型的变量,那么您将需要类型擦除,并且必须创建一个AnyRepository.