iOS/Sources/App/Settings/Eureka/RealmSection.swift

107 lines
3.1 KiB
Swift

import Eureka
import Foundation
import RealmSwift
import Shared
public final class RealmSection<ObjectType: Object>: Section {
private let collection: AnyRealmCollection<ObjectType>
private let getter: (ObjectType) -> [BaseRow]?
private let emptyRows: [BaseRow]
private let didUpdate: (Section, AnyRealmCollection<ObjectType>) -> Void
private var observeTokens: [NotificationToken] = []
convenience init(
header: String? = nil,
footer: String? = nil,
collection: AnyRealmCollection<ObjectType>,
emptyRows: [BaseRow] = [],
getter: @escaping (ObjectType) -> BaseRow?,
didUpdate: @escaping (Section, AnyRealmCollection<ObjectType>) -> Void = { _, _ in }
) {
self.init(
header: header,
footer: footer,
collection: collection,
emptyRows: emptyRows,
getter: { getter($0).flatMap { [$0] } },
didUpdate: didUpdate
)
}
init(
header: String? = nil,
footer: String? = nil,
collection: AnyRealmCollection<ObjectType>,
emptyRows: [BaseRow] = [],
getter: @escaping (ObjectType) -> [BaseRow]?,
didUpdate: @escaping (Section, AnyRealmCollection<ObjectType>) -> Void = { _, _ in }
) {
self.collection = collection
self.emptyRows = emptyRows
self.getter = getter
self.didUpdate = didUpdate
super.init(header: header, footer: footer)
let observeToken = collection.observe { [weak self] change in
switch change {
case .initial:
// defering a run loop here causes weird ordering issues with eureka, makes the onChange not fire
break
case .update:
self?.update()
case .error:
break
}
}
observeTokens.append(observeToken)
UIView.performWithoutAnimation {
update()
}
}
deinit {
observeTokens.forEach { $0.invalidate() }
}
@available(*, unavailable)
required init(_ elements: some Sequence<BaseRow>) {
fatalError("init(_:) has not been implemented")
}
@available(*, unavailable)
required init() {
fatalError("init() has not been implemented")
}
private func update() {
let tableView = (form?.delegate as? FormViewController)?.tableView
tableView?.beginUpdates()
removeAll()
if collection.isEmpty {
append(contentsOf: emptyRows)
} else {
let appendingRows = collection.compactMap { (object: ObjectType) -> [BaseRow]? in
let rows = self.getter(object)
// we rely on remove/insert, and eureka doesn't handle this -- even if it's the same === row!
rows?.forEach { $0.tag = nil }
return rows
}.flatMap { $0 }
// note: eureka has a bug if we don't wrap this in Array where it duplicates
append(contentsOf: Array(appendingRows))
}
didUpdate(self, collection)
evaluateHidden()
reload()
tableView?.endUpdates()
}
}