I am trying to figure out the cleanest way to integrate Realm into our MVVM architecture.
I like to use a Realm Results<Model> object within my ViewModel that represents all Models to be displayed by the View (i.e. UIViewController).
Using and listening to changes in Results<Model> directly in the View works flawlessly, but seems to break MVVM conventions (the View should not be directly exposed to instances of Models).
I also like the lazy nature of the Results and other Realm collections which is effectively gone when using
let viewModels: [ViewModel] = results.map { object in
return ViewModel(primaryKey: object.primaryKeyValue
}
// viewModels is not lazy anymore ;(
That said, I'd like to see a way to basically use the Results collection but lazily map objects within the collection to another type (i.e. ViewModel) on demand.
let viewModels: Results<ViewModel> = results.mapLazily { object in
return ViewModel(primaryKey: object.primaryKeyValue)
}
This would make it easier to have a clean architecture and not expose models directly to the View, but still leverage the lazyness of a Realm collection.
Here is some contrived example on how this mapLazily operator might work:
// PersonListViewModel (for a Person collection)
class PersonListViewModel {
var people: Result<PersonViewModel> {
let results: Result<Person> = ...
// Maps the result of Person objects to PersonViewModel objects on demand
return results.mapLazily { object in
return PersonViewModel(primaryKey: object.primaryKeyValue)
}
}
}
// Model
class Person: Object {
dynamic var identifier: String = ""
dynamic var name: String = ""
var primaryKeyValue: String {
return identifier
}
}
// ViewModel for a Person
class PersonViewModel {
private let primayKey: String
private let realm: Realm
private var model: Person? {
return realm.objectForPrimaryKey(Person.self, key: primaryKey)
}
init(primaryKey: String, realm: Realm) {
self.primaryKey = primaryKey
self.realm = realm
}
var formattedName: String? {
return model?.name.lowercase
}
}
A lazy RealmCollection.map would be immensely useful. I thought we were tracking that somewhere (maybe called "projections"), but I can't find an issue for it at the moment.
One design issue with this is that we can't return a Results from this operation because many operations on Results wouldn't be available on this lazy collection (such as filter, sum, etc.).
@aschuch does renaming this ticket to "Offer a lazy RealmCollection.map function" accurately and sufficiently describe what you're proposing?
@jpsim Yes, that title describes this feature request much more accurately.
Agreeing with the author, this feature is greatly desired. YOUGE benefit for simple apps/demonstrations - where if write MVVM, no need to additionally pull in a reactive framework.
Pity if such valuable feature isn't getting traction - @aschuch or is it?
When you say you'd like to avoid exposing models to the view, is it because you want additional functionality in the projected view model or simply because you'd like to adhere to MVVM guidelines by the letter?
To somehow solve this... i made a "box" that receive a object and a lambda in kotlin (not sure how it should look like in swift) :
https://gist.github.com/Grohden/242da5d7ae7bf8aa3a34d6afcf7789fa
Of course this is just simple, i don't deal with threading and other things.. also, for my case, the map results in a object that occupy more memory than the original, so i keep a WeakReference, but you could 'switch' to the mapped object (keep a strong reference) and null original the references after that...
Most helpful comment
@jpsim Yes, that title describes this feature request much more accurately.