I want to create two Realm model classes and one protocol, which is adopted by the two model class. For example:
class Dog: Object, Animal {
dynamic var name = ""
}
class Cat: Object, Animal {
dynamic var name = ""
}
protocol Animal {
var name: String { get }
}
In this case, I created two model class and one protocol.
However, when I moved to the implementation, the problem occurred. The code below is written in view controller:
var dogs: Results<Dog>? {
return try! Realm().objects(Dog)
}
var cats: Results<Cat> {
return try! Realm().objects(Cat)
}
This code does not have any problems. But the code below:
var animals: Results<Animal>? {
switch currentSegmented { // this is from UISegmentedControl
case .Cat: // this is from enum
return self.cats
case .Dog:
return self.dogs
}
is not compiled with the error: Results requires that Animal inherit from Object
.
However, Animal
is a protocol and thus cannot be inherited from Object
.
Is it still possible to utilize the protocol here?
I don't think there's a nice solution. User-defined generics in Swift are invariant, so even if Animal
is a class you can't convert Results<Dog>
to Results<Animal>
.
The unpleasantly verbose solution is to create an explicit wrapper type around your different kinds of Results:
enum AnimalResultsEnum {
case DogResults(dogs: Results<Dog>)
case CatResults(cats: Results<Cat>)
}
class AnimalResults {
var animals = AnimalResultsEnum.DogResults(dogs: try! Realm().objects(Dog))
var realm: Realm? {
switch animals {
case .DogResults(let dogs):
return dogs.realm
case .CatResults(let cats):
return cats.realm
}
}
var count: Int {
switch animals {
case .DogResults(let dogs):
return dogs.count
case .CatResults(let cats):
return cats.count
}
}
subscript(index: Int) -> Animal {
switch animals {
case .DogResults(let dogs):
return dogs[index]
case .CatResults(let cats):
return cats[index]
}
}
// ... wrap the rest of the methods needed ...
}
You can make this generic by instead creating a semi-type-erased container to wrap Results:
class CovariantResults<T: Object> {
private var base: _CovariantResultsBase<T>
init<U: Object>(_ inner: Results<U>) {
base = _CovariantResultsImpl<T, U>(inner)
}
subscript(index: Int) -> T {
return base[index]
}
// ... wrap the rest of the methods needed ...
}
class _CovariantResultsBase<T: Object> {
subscript(index: Int) -> T { fatalError("abstract") }
// ... wrap the rest of the methods needed ...
}
class _CovariantResultsImpl<T: Object, U: Object>: _CovariantResultsBase<T> {
private let impl: Results<U>
init(_ inner: Results<U>) {
impl = inner
}
override subscript(index: Int) -> T {
return impl[index] as! T
}
// ... wrap the rest of the methods needed ...
}
// Used as:
let animals = CovariantResults<Animal>(try! Realm().objects(Dog))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With