Reputation: 183
I try to create universal UITableView dataSource class for realm Results, but when i try to send realmResults different from Results<<>Object<>> on example Results i got this error message:
Cannot convert value of type 'Results'<'Project'>' to expected element type 'Results'<'Object'>'
class RealmResultsTableViewDataSource: TableViewDataSource {
var realmResults:[Results<Object>]
var notificationTokens: [NotificationToken] = []
init(tableView: UITableView, realmResults: [Results<Object>], configCell: @escaping TableViewConfigCellBlock, canEdit: TableViewCanEditCellBlock?, canMove: TableViewCanMoveRowBlock?, commitEditing: TableViewCommitEditingStyleBlock?) {
self.realmResults = realmResults
super.init(tableView: tableView, dataSource: [], configCell: configCell, canEdit: canEdit, canMove: canMove, commitEditing: commitEditing)
addTokens(for: realmResults)
}
deinit {
for token in notificationTokens {
token.stop()
}
}
override var sections: Int{
get { return realmResults.count }
}
override func numberOfRows(section: Int) -> Int {
return realmResults[section].count
}
// MARK: Add tokens
func addTokens(for results: [Results<Object>]) {
for result in results {
let notificationToken = result.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
tableView.reloadData()
break
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.endUpdates()
break
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
break
}
}
notificationTokens.append(notificationToken)
}
}
override func dataSourceObject(on indexPath: IndexPath) -> Any {
return realmResults[indexPath.section][indexPath.row]
}
}
And:
let realm = try! Realm()
let result = realm.objects(Project.self).filter("id < 10").sorted(byKeyPath: "id", ascending: true)
tableViewDataSource = RealmResultsTableViewDataSource(tableView: tableView, realmResults: [result], configCell: { (tableView, indexPath, object) -> UITableViewCell in
let cell = tableView.dequeueReusableCell(withIdentifier:ProjectListTableViewCell.cellIdentifier , for: indexPath)
return cell
}, canEdit: nil, canMove: nil, commitEditing: nil)
import RealmSwift
class Project: Object {
dynamic var id: Int = 0
dynamic var name: String = ""
}
Solution:
class RealmResultsTableViewDataSource<T: Object>: TableViewDataSource {
var realmResults:Results<T>
var notificationTokens: [NotificationToken] = []
init(tableView: UITableView, realmResults: Results<T>, configCell: @escaping TableViewConfigCellBlock, canEdit: TableViewCanEditCellBlock?, canMove: TableViewCanMoveRowBlock?, commitEditing: TableViewCommitEditingStyleBlock?) {
self.realmResults = realmResults
super.init(tableView: tableView, dataSource: [], configCell: configCell, canEdit: canEdit, canMove: canMove, commitEditing: commitEditing)
addTokens(for: self.realmResults)
}
deinit {
for token in notificationTokens {
token.stop()
}
}
override var sections: Int{
get{
return 1
}
}
override func numberOfRows(section: Int) -> Int {
return realmResults.count
}
// MARK: Add tokens
func addTokens(for results: Results<T>) {
let notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
guard let tableView = self?.tableView else { return }
guard tableView.dataSource === self else { return }
switch changes {
case .initial:
// Results are now populated and can be accessed without blocking the UI
tableView.reloadData()
break
case .update(_, let deletions, let insertions, let modifications):
// Query results have changed, so apply them to the UITableView
tableView.beginUpdates()
tableView.insertRows(at: insertions.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: 0)}),
with: .automatic)
tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: 0) }),
with: .automatic)
tableView.endUpdates()
break
case .error(let error):
// An error occurred while opening the Realm file on the background worker thread
fatalError("\(error)")
break
}
}
notificationTokens.append(notificationToken)
}
override func dataSourceObject(on indexPath: IndexPath) -> Any {
return realmResults[indexPath.row]
}
override func removeObject(indexPath: IndexPath) {
// assertionFailure("you can use ramoveObject int \(String(describing: self))")
}
}
Upvotes: 1
Views: 427
Reputation: 5303
AFAIK, Realm Swift does not support polymorphism, yet.
The current solution is to use composition instead of inheritance. There are lots of good argument about it (Gang of Four and Joshua Bloch to mention some prestigious supporters of this approach).
On the other hand we are considering to allow inheritance, but it would not allow queries. For example: Animal which is extended both by Dog, Cat and Duck. You would not be able to query Animals with for legs and have all Dogs and Cats, but not Ducks. We feel this would be a very crippling behavior but are eager to listen to more opinions.
This is discussed here (this discuss the java version of the product, so it's only a starting point for you...): https://github.com/realm/realm-java/issues/761
The simplest solution [but perhaps not the best if you need to reuse these methods with other subclasses of Object] for you would be to replace "Object" with "Project"
Upvotes: 1