From ca6ecd13aad2c87608f064f9a31ff383d78d9bae Mon Sep 17 00:00:00 2001 From: shannon Date: Tue, 10 Dec 2024 11:16:43 -0500 Subject: [PATCH] Make sure not to display an image that belongs to a different post MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The key change here is to store the image subscription in the MediaView’s disposeBag rather than the configuration’s, so that it is properly disposed of in the MediaView’s prepareForReuse(). For good measure, also adding MainActor on prepareForReuse and actual cancellation of all subscriptions before releasing them. Fixes #1374 [BUG] Posts can load the wrong images --- .../MastodonUI/View/Content/MediaView.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift b/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift index 27c9e29c6e..a23fc4d555 100644 --- a/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift +++ b/MastodonSDK/Sources/MastodonUI/View/Content/MediaView.swift @@ -127,7 +127,8 @@ extension MediaView { layoutAlt() } - private func bindImage(configuration: Configuration, info: Configuration.ImageInfo) { + private func bindImage(configuration: Configuration, info: Configuration.ImageInfo) { + let subscribedConfigurationIdentifier = ObjectIdentifier(configuration) // this shouldn't be necessary now, but allows a check in debug mode. https://github.com/mastodon/mastodon-ios/issues/1374 Publishers.CombineLatest( configuration.$previewImage, configuration.$blurhashImage @@ -135,13 +136,16 @@ extension MediaView { .receive(on: DispatchQueue.main) .sink { [weak self] previewImage, blurhashImage in guard let self = self else { return } - + guard let currentConfiguration = self.configuration, ObjectIdentifier(currentConfiguration) == subscribedConfigurationIdentifier else { + assert(false, "\(self) attempt to load an image that belongs to a configuration no longer associated with this MediaView.") + return + } let image = configuration.isReveal ? (previewImage ?? blurhashImage ?? MediaView.placeholderImage) : (blurhashImage ?? MediaView.placeholderImage) self.imageView.image = image } - .store(in: &configuration.disposeBag) + .store(in: &_disposeBag) bindAlt(configuration: configuration, altDescription: info.altDescription) } @@ -220,7 +224,11 @@ extension MediaView { overlayViewController.view.pinToParent() } + @MainActor public func prepareForReuse() { + for cancellable in _disposeBag { + cancellable.cancel() + } _disposeBag.removeAll() // reset appearance