Differ
Differ copied to clipboard
`NSInternalInconsistencyException` using MVVM approach
Hi and thank you for this library. I am new to diffing. I tested the example project and I am now trying to use Diffing with MMVM.
Here is the simple MVVM code I am using to try it:
protocol InteractorDelegate: class {
func viewModelDidChange(_ old: ViewModel?, _ new: ViewModel)
}
class Interactor {
weak var delegate: InteractorDelegate?
var items = [
[1,5,6,7,4,6,7,1,5],
[1,5,2,1,0,6,7],
]
var viewModel: ViewModel? {
didSet {
delegate?.viewModelDidChange(oldValue, viewModel!)
}
}
var currentObjects: Int = 0 {
didSet {
viewModel = .init(with: .loaded(items[currentObjects]))
}
}
init() {
viewModel = .init(with: .initialized)
}
func fetchValue() {
currentObjects = currentObjects == 0 ? 1 : 0
}
}
struct ViewModel {
enum ViewModelType: Equatable {
case cell(CellViewModel)
}
enum State {
case initialized
case loaded([Int])
}
let state: State
let viewModels: [ViewModelType]
init(with state: State) {
self.state = state
switch state {
case .initialized: viewModels = []
case .loaded(let values):
viewModels = CellViewModel.from(values).map(ViewModelType.cell)
}
}
}
extension ViewModel: Equatable {
static func ==(left: ViewModel, right: ViewModel) -> Bool {
return left.state == left.state
}
}
extension ViewModel.State: Equatable {
static func ==(left: ViewModel.State, right: ViewModel.State) -> Bool {
switch (left, right) {
case (.initialized, .initialized): return true
case let (.loaded(l), .loaded(r)): return l == r
default: return false
}
}
}
struct CellViewModel {
let description: String
}
extension CellViewModel {
static func from(_ values: [Int]) -> [CellViewModel] {
return values.map { CellViewModel(description: String($0)) }
}
}
extension CellViewModel: Equatable {
static func ==(left: CellViewModel, right: CellViewModel) -> Bool {
return left.description == right.description
}
}
Here for the Controller
part:
import UIKit
import Differ
class ViewController: UIViewController {
...
override func viewDidLoad() {
super.viewDidLoad()
...
interactor.fetchValue()
}
@objc
func onRefresh() {
interactor.fetchValue()
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return interactor.viewModel.value.viewModels.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellViewModel = interactor.viewModel.value.viewModels[indexPath.row]
switch cellViewModel {
case .cell(let viewModel):
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = viewModel.description
return cell
}
}
}
extension ViewController: InteractorDelegate {
func viewModelDidChange(_ old: ViewModel?, _ new: ViewModel) {
if let prev = old {
print("Previous => State: \(prev.state) | ViewModelType.count: \(prev.viewModels.count)")
} else {
print("Previous => State: nil | ViewModelType.count: nil")
}
print("Current => State: \(new.state) | ViewModelType.count: \(new.viewModels.count)")
DispatchQueue.main.async {
self.tableView.animateRowChanges(oldData: old?.viewModels ?? [], newData: new.viewModels)
}
}
}
Here is what I got:
Previous => State: initialized | ViewModelType.count: 0
Current => State: loaded([1, 5, 2, 1, 0, 6, 7]) | ViewModelType.count: 7
2019-10-29 13:45:56.636678+0900 TestDiffer[93631:21379549] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (7) must be equal to the number of rows contained in that section before the update (7), plus or minus the number of rows inserted or deleted from that section (7 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
I first posted the question on stack overflow but maybe this is where it should be asked.
Is there something wrong with Equatable
or am I missing something?
Thank you.