Skip to content

Commit

Permalink
Release cell reference on prepareForReuse as a fix for iOS 15+ cell…
Browse files Browse the repository at this point in the history
… lifecycle changes. On iOS 15+, for performance reasons, the table view data source may create cells ahead of time using the `cellForRow` method. So the cell may be created but never go through the whole `willDisplayCell/didEndDisplaying` lifecycle callbacks as it may never be displayed. However, we start loading the cell image on `cellForRow` and only cancel the request on `didEndDisplaying`. In such cases, there can be a race condition when reusing a cell that was never displayed because the request would carry on and potentially load the wrong image at the wrong index path.
  • Loading branch information
caiozullo committed Feb 2, 2023
1 parent 34db55b commit 242a2c0
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 0 deletions.
15 changes: 15 additions & 0 deletions EssentialApp/EssentialAppTests/FeedUIIntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,21 @@ class FeedUIIntegrationTests: XCTestCase {
XCTAssertEqual(view0?.isShowingImageLoadingIndicator, false, "Expected no loading indicator when image loads successfully after view becomes visible again")
}

func test_feedImageView_doesNotShowDataFromPreviousRequestWhenCellIsReused() throws {
let (sut, loader) = makeSUT()

sut.loadViewIfNeeded()
loader.completeFeedLoading(with: [makeImage(), makeImage()])

let view0 = try XCTUnwrap(sut.simulateFeedImageViewVisible(at: 0))
view0.prepareForReuse()

let imageData0 = UIImage.make(withColor: .red).pngData()!
loader.completeImageLoading(with: imageData0, at: 0)

XCTAssertEqual(view0.renderedImage, .none, "Expected no image state change for reused view once image loading completes successfully")
}

func test_feedImageView_doesNotRenderLoadedImageWhenNotVisibleAnymore() {
let (sut, loader) = makeSUT()
sut.loadViewIfNeeded()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ extension FeedImageCellController: UITableViewDataSource, UITableViewDelegate, U
cell?.onRetry = { [weak self] in
self?.delegate.didRequestImage()
}
cell?.onReuse = { [weak self] in
self?.releaseCellForReuse()
}
delegate.didRequestImage()
return cell!
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ public final class FeedImageCell: UITableViewCell {
@IBOutlet private(set) public var descriptionLabel: UILabel!

var onRetry: (() -> Void)?
var onReuse: (() -> Void)?

@IBAction private func retryButtonTapped() {
onRetry?()
}

public override func prepareForReuse() {
super.prepareForReuse()

onReuse?()
}
}

0 comments on commit 242a2c0

Please sign in to comment.