From 48be1bea6bec3f9871390d744853b985f6f58912 Mon Sep 17 00:00:00 2001 From: Caio Zullo Date: Thu, 2 Nov 2023 13:18:15 +0200 Subject: [PATCH] Ensure previous cells' prepareForReuse doesn't affect new cells by removing the `onReuse` closure reference when releasing the cell for reuse. Every time we update the table view state, existing cells can be reused for different index paths, where they'll be managed by different cell controllers - so we need to remove all references to the previous cell controllers when releasing the cell for reuse. Since the `onReuse` closure references the cell controller via `self`, we need to set `onReuse` to `nil` when releasing the cell for reuse by another cell controller. This way, we ensure there are no references to the cell controller so there will be no side effects. --- .../FeedUIIntegrationTests.swift | 17 +++++++++++++++++ .../Controllers/FeedImageCellController.swift | 1 + 2 files changed, 18 insertions(+) diff --git a/EssentialApp/EssentialAppTests/FeedUIIntegrationTests.swift b/EssentialApp/EssentialAppTests/FeedUIIntegrationTests.swift index 793ee3c1..6b2f652c 100644 --- a/EssentialApp/EssentialAppTests/FeedUIIntegrationTests.swift +++ b/EssentialApp/EssentialAppTests/FeedUIIntegrationTests.swift @@ -518,6 +518,23 @@ class FeedUIIntegrationTests: XCTestCase { XCTAssertEqual(view0.renderedImage, .none, "Expected no image state change for reused view once image loading completes successfully") } + func test_feedImageView_showsDataForNewViewRequestAfterPreviousViewIsReused() throws { + let (sut, loader) = makeSUT() + + sut.simulateAppearance() + loader.completeFeedLoading(with: [makeImage(), makeImage()]) + + let previousView = try XCTUnwrap(sut.simulateFeedImageViewNotVisible(at: 0)) + + let newView = try XCTUnwrap(sut.simulateFeedImageViewVisible(at: 0)) + previousView.prepareForReuse() + + let imageData = UIImage.make(withColor: .red).pngData()! + loader.completeImageLoading(with: imageData, at: 1) + + XCTAssertEqual(newView.renderedImage, imageData) + } + func test_feedImageView_doesNotRenderLoadedImageWhenNotVisibleAnymore() { let (sut, loader) = makeSUT() sut.simulateAppearance() diff --git a/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift b/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift index 7fff93a1..edfe9ee8 100644 --- a/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift +++ b/EssentialFeed/EssentialFeediOS/Feed UI/Controllers/FeedImageCellController.swift @@ -76,6 +76,7 @@ extension FeedImageCellController: UITableViewDataSource, UITableViewDelegate, U } private func releaseCellForReuse() { + cell?.onReuse = nil cell = nil } }