Skip to content

Failed notification image downloads are never retried within a session #25786

Description

@jkmassel

Description

The notification detail image cell (NoteBlockImageTableViewCell) never retries a remote image download that fails on its first attempt. If the request fails — flaky network, transient 5xx, offline — the cell is left blank for the rest of that viewing session, even after connectivity is restored. There is no failure: handler wired up and no retry at this layer (the retry loop in NotificationMediaDownloader is a separate code path this cell does not use).

  • Expected: a failed image load recovers — retried when connectivity returns or the cell is reconfigured, or at minimum not permanently stuck for the session.
  • Actual: the image area stays empty until the cell is reused or bound to a different URL.

Root Cause

NoteBlockImageTableViewCell.downloadImage(_:) sets imageURL optimistically, before the download resolves, and never resets it on failure:

@objc func downloadImage(_ url: URL?) {
    guard imageURL != url else {
        return           // ← short-circuits a same-URL re-bind
    }
    imageURL = url       // ← set before the download completes; never cleared on failure
    blockImageView.downloadImage(from: url)
}

The imageURL != url guard exists to skip redundant re-downloads of an image we're already showing. But because imageURL is assigned up front, the guard cannot distinguish "already showing this image" from "attempted this image and failed." Once a download fails, any later setupImageCell call with the same URL hits the guard and returns early — the failed download is never re-attempted.

UIImageView.downloadImage(from:) does call failure?(error) on the failure path, but the cell passes no failure: closure, so there is no hook to reset state. Recovery only happens when the cell is genuinely reused (prepareForReuse resets imageURL = nil) — e.g. navigating away and back — or when it is bound to a different URL.

Step-by-step reproduction instructions

  1. Enable Airplane Mode (or a Network Link Conditioner profile with 100% loss).
  2. Open a notification whose detail view contains a remote image (e.g. an achievement / milestone notification).
  3. Observe the image area is blank — the download failed.
  4. Disable Airplane Mode and wait.
  5. The image never appears, despite connectivity being restored, as long as you stay on the screen.
  6. Navigate away and back — the image now loads (the cell was reused).

Screenshots, screen recording, code snippet

See the snippet under Root Cause. File: WordPress/Classes/ViewRelated/Notifications/Views/NoteBlockImageTableViewCell.swift.

Scope / notes

  • Pre-existing. Surfaced during code review of the notification image spring-animation removal (branch bugfix/CMM-2157-notification-animation-v1); not introduced by that change.
  • Confirmed by code inspection; not yet reproduced on-device.
  • Fix sketch: pass a failure: closure to blockImageView.downloadImage(from:) that resets imageURL = nil, so a later re-bind retries. Keep the optimistic assignment for in-flight dedupe — the image-view layer already calls cancelImageDownload() on re-entry. Moving the assignment entirely into the success path would fix retries but lose cell-level dedupe of concurrent same-URL calls, so the failure:-reset approach is the narrower fix.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions