Skip to content

Commit 2748e87

Browse files
Merge pull request #51 from pkalinowski/fix_updating_tableview_cell
#41 fixed a bug with not updating cell content after moving it
2 parents 331d219 + fcc1d15 commit 2748e87

File tree

5 files changed

+150
-15
lines changed

5 files changed

+150
-15
lines changed

Sourcing/FetchedResultsDataProvider.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ open class FetchedResultsDataProvider<Object: NSFetchRequestResult>: NSObject, N
103103

104104
public func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
105105
dataProviderDidChangeContets(with: updates)
106+
let updatesByMoves = updates.map({ (operation: DataProviderUpdate<Object>) -> DataProviderUpdate<Object>? in
107+
if case .move(_, let newIndexPath) = operation {
108+
return .update(newIndexPath, object(at: newIndexPath))
109+
}
110+
return nil
111+
}).flatMap { $0 }
112+
dataProviderDidChangeContets(with: updatesByMoves)
106113
}
107114

108115
func dataProviderDidChangeContets(with updates: [DataProviderUpdate<Object>]?, triggerdByTableView: Bool = false) {

SourcingTests/CDTrain+CoreDataProperties.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ import Foundation
1313
import CoreData
1414

1515
extension CDTrain {
16+
17+
@nonobjc public class func fetchRequest() -> NSFetchRequest<CDTrain> {
18+
return NSFetchRequest<CDTrain>(entityName: "CDTrain")
19+
}
20+
1621
@NSManaged var id: String
1722
@NSManaged var name: String
1823
}

SourcingTests/CDTrain.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,12 @@ import Foundation
1010
import CoreData
1111

1212
class CDTrain: NSManagedObject {
13+
14+
private static let entityName = String(describing: CDTrain.self)
15+
16+
public class func newObject(in managedContext: NSManagedObjectContext) -> CDTrain {
17+
// swiftlint:disable force_unwrapping
18+
let entityDesc = NSEntityDescription.entity(forEntityName: entityName, in: managedContext)!
19+
return CDTrain(entity: entityDesc, insertInto: managedContext)
20+
}
1321
}

SourcingTests/CollectionViewDataSource_SingleCellTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import XCTest
3030
import UIKit
3131
@testable import Sourcing
3232

33-
// swiftlint:disable force_cast force_unwrapping
33+
// swiftlint:disable force_cast force_unwrapping xctfail_message
3434
class CollectionViewDataSourceSingleCellTest: XCTestCase {
3535

3636
let cellIdentifier = "cellIdentifier"

SourcingTests/FetchedResultsDataProviderTests.swift

Lines changed: 129 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import XCTest
2929
import CoreData
3030
@testable import Sourcing
3131

32-
// swiftlint:disable force_cast force_try force_unwrapping
32+
// swiftlint:disable force_cast force_try force_unwrapping xctfail_message
3333
class FetchedResultsDataProviderTests: XCTestCase {
3434
func managedObjectContextForTesting() -> NSManagedObjectContext {
3535
let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
@@ -42,46 +42,61 @@ class FetchedResultsDataProviderTests: XCTestCase {
4242
}
4343

4444
var managedObjectContext: NSManagedObjectContext!
45-
var train: CDTrain!
45+
var train1: CDTrain!
46+
var train2: CDTrain!
4647
var fetchedResultsController: NSFetchedResultsController<CDTrain>!
4748
var dataProvider: FetchedResultsDataProvider<CDTrain>!
4849

4950
override func setUp() {
5051
managedObjectContext = managedObjectContextForTesting()
5152

52-
train = NSEntityDescription.insertNewObject(forEntityName: "CDTrain", into: managedObjectContext) as! CDTrain
53-
train.id = "1"
54-
train.name = "ICE"
55-
managedObjectContext.insert(train)
53+
train1 = self.train(id: "1", name: "ICE")
54+
managedObjectContext.insert(train1)
5655

57-
let fetchReuqest = NSFetchRequest<CDTrain>(entityName: "CDTrain")
58-
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
56+
train2 = self.train(id: "2", name: "ICE")
57+
managedObjectContext.insert(train2)
58+
59+
let fetchReuqest: NSFetchRequest<CDTrain> = CDTrain.fetchRequest()
60+
let sortDescriptor = NSSortDescriptor(key: #keyPath(CDTrain.id), ascending: true)
5961
fetchReuqest.sortDescriptors = [sortDescriptor]
6062
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchReuqest, managedObjectContext: managedObjectContext,
61-
sectionNameKeyPath: "name", cacheName: nil)
63+
sectionNameKeyPath: #keyPath(CDTrain.name), cacheName: nil)
6264
dataProvider = try! FetchedResultsDataProvider(fetchedResultsController: fetchedResultsController, dataProviderDidUpdate: { _ in })
6365
}
6466

67+
private func train(id: String, name: String) -> CDTrain {
68+
let train = CDTrain.newObject(in: managedObjectContext)
69+
train.id = id
70+
train.name = name
71+
return train
72+
}
73+
6574
func testNumberOfSections() {
6675
//Then
6776
XCTAssertEqual(dataProvider.numberOfSections(), 1)
6877
}
6978

7079
func testNumberOfItems() {
7180
//Then
72-
XCTAssertEqual(dataProvider.numberOfItems(inSection: 0), 1)
81+
XCTAssertEqual(dataProvider.numberOfItems(inSection: 0), 2)
7382
}
7483

7584
func testObjectAtIndexPath() {
7685
//Then
77-
let indexPath = IndexPath(item: 0, section: 0)
78-
XCTAssertEqual(dataProvider.object(at: indexPath), train)
86+
var indexPath = IndexPath(item: 0, section: 0)
87+
XCTAssertEqual(dataProvider.object(at: indexPath), train1)
88+
89+
indexPath = IndexPath(item: 1, section: 0)
90+
XCTAssertEqual(dataProvider.object(at: indexPath), train2)
7991
}
8092

8193
func testIndexPathForObject() {
8294
//Then
83-
let indexPath = IndexPath(item: 0, section: 0)
84-
XCTAssertEqual(dataProvider.indexPath(for: train), indexPath)
95+
var indexPath = IndexPath(item: 0, section: 0)
96+
XCTAssertEqual(dataProvider.indexPath(for: train1), indexPath)
97+
98+
indexPath = IndexPath(item: 1, section: 0)
99+
XCTAssertEqual(dataProvider.indexPath(for: train2), indexPath)
85100
}
86101

87102
func testReconfigureFetchRequest() {
@@ -201,6 +216,106 @@ class FetchedResultsDataProviderTests: XCTestCase {
201216
XCTAssert(didUpdateDataSource)
202217
}
203218

219+
func testProcessUpdatesForMoveChange() {
220+
//Given
221+
var updateIndexPath = IndexPath()
222+
var moveIndexPaths = [IndexPath]()
223+
224+
dataProvider = try! FetchedResultsDataProvider(fetchedResultsController: fetchedResultsController, dataProviderDidUpdate: { _ in })
225+
dataProvider.whenDataProviderChanged = { updates in
226+
guard let updates = updates, !updates.isEmpty else {
227+
return
228+
}
229+
230+
for update in updates {
231+
switch update {
232+
case .update(let indexPath, _):
233+
updateIndexPath = indexPath
234+
case .move(let indexPath, let newIndexPath):
235+
moveIndexPaths = [indexPath, newIndexPath]
236+
default: break
237+
}
238+
}
239+
}
240+
241+
let oldIndexPath = IndexPath(row: 0, section: 0)
242+
let newIndexPath = IndexPath(row: 1, section: 0)
243+
dataProvider.controller(fetchedResultsController as! NSFetchedResultsController<NSFetchRequestResult>,
244+
didChange: 1, at: oldIndexPath, for: .move, newIndexPath: newIndexPath)
245+
246+
//When
247+
dataProvider.controllerDidChangeContent(fetchedResultsController as! NSFetchedResultsController<NSFetchRequestResult>)
248+
249+
//Then
250+
XCTAssertEqual(updateIndexPath, newIndexPath)
251+
XCTAssertEqual(moveIndexPaths, [oldIndexPath, newIndexPath])
252+
}
253+
254+
func testProcessUpdatesOrderForMoveChange() {
255+
var updatesNumber = 0
256+
var isFirstMoveCall = false
257+
var isSecondUpdateCall = false
258+
259+
dataProvider = try! FetchedResultsDataProvider(fetchedResultsController: fetchedResultsController, dataProviderDidUpdate: { _ in })
260+
dataProvider.whenDataProviderChanged = { updates in
261+
guard let updates = updates, !updates.isEmpty else {
262+
return
263+
}
264+
265+
updatesNumber += 1
266+
267+
if case .move(_, _) = updates.first! {
268+
isFirstMoveCall = true
269+
}
270+
271+
if case .update(_, _) = updates.first! {
272+
if isFirstMoveCall {
273+
isSecondUpdateCall = true
274+
}
275+
}
276+
277+
}
278+
279+
let oldIndexPath = IndexPath(row: 0, section: 0)
280+
let newIndexPath = IndexPath(row: 1, section: 0)
281+
dataProvider.controller(fetchedResultsController as! NSFetchedResultsController<NSFetchRequestResult>,
282+
didChange: 1, at: oldIndexPath, for: .move, newIndexPath: newIndexPath)
283+
284+
//When
285+
dataProvider.controllerDidChangeContent(fetchedResultsController as! NSFetchedResultsController<NSFetchRequestResult>)
286+
287+
//Then
288+
XCTAssertEqual(updatesNumber, 2)
289+
XCTAssert(isFirstMoveCall)
290+
XCTAssert(isSecondUpdateCall)
291+
}
292+
293+
func testProcessUpdatesForUpdateChange() {
294+
//Given
295+
var updateIndexPath = IndexPath()
296+
297+
dataProvider = try! FetchedResultsDataProvider(fetchedResultsController: fetchedResultsController, dataProviderDidUpdate: { _ in })
298+
dataProvider.whenDataProviderChanged = { updates in
299+
guard let updates = updates, !updates.isEmpty else {
300+
return
301+
}
302+
303+
if case .update(let indexPath, _) = updates.first! {
304+
updateIndexPath = indexPath
305+
}
306+
}
307+
308+
let indexPath = IndexPath(row: 1, section: 0)
309+
dataProvider.controller(fetchedResultsController as! NSFetchedResultsController<NSFetchRequestResult>,
310+
didChange: 1, at: indexPath, for: .update, newIndexPath: indexPath)
311+
312+
//When
313+
dataProvider.controllerDidChangeContent(fetchedResultsController as! NSFetchedResultsController<NSFetchRequestResult>)
314+
315+
//Then
316+
XCTAssertEqual(updateIndexPath, indexPath)
317+
}
318+
204319
func testWillChangeContent() {
205320
//Given
206321
let oldIndexPath = IndexPath(row: 0, section: 0)

0 commit comments

Comments
 (0)