Skip to content

refactor(mobile): cleanup iOS image loading pipeline#27672

Merged
alextran1502 merged 17 commits intomainfrom
refactor/ios-image-request-base
Apr 10, 2026
Merged

refactor(mobile): cleanup iOS image loading pipeline#27672
alextran1502 merged 17 commits intomainfrom
refactor/ios-image-request-base

Conversation

@LeLunZ
Copy link
Copy Markdown
Collaborator

@LeLunZ LeLunZ commented Apr 9, 2026

Description

First this adds a ImageRequest base class, which holds the completion callback and the isCancelled property (isCancelled is thread safe)
Second this moves the encoded data block out of the BlockOperation of the remote image request. At this point it's only copying some data, so nothing resource intensive and worth starting an operation for.

This PR should fix all these issues:

  • previously calling cancel on a remote image request didn't cancel it when the http request part was already done, as it was already removed from the request registry
  • when calling cancelRequest for a remote image request which is still doing the http request, we previously didn't invoke the completion callback
  • when calling cancelRequest for a local image request which is still queued (not yet running), we previously didn't invoke the completion callback

Drawbacks:
This PR removes cancelling BlockOperations for now. This is needed because a still queued but cancelled BlockOperation wouldn't launch a new thread, and thus wouldn't call our cancel completion callback.
This is a performance regression, as now we the system has to dispatch the block operation just to afterwards check the isCancelled flag.

I plan to fix the regression, @mertalev and me are still in discussion on what's the best way.
This for now fixes all issues I could find.

How Has This Been Tested?

  • iOS simulator

Checklist:

Please describe to which degree, if any, an LLM was used in creating this pull request.

used LLM for questions about the Swift Concurrency (@sendable etc.)


private static func handleCompletion(requestId: Int64, encoded: Bool, data: Data?, response: URLResponse?, error: Error?) {
guard let request = registry.remove(requestId: requestId) else {
guard let request = registry.get(requestId: requestId) else {
Copy link
Copy Markdown
Collaborator Author

@LeLunZ LeLunZ Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here we had two issues:

  • if request had been cancelled before the http part is finished, the request wouldn't be in the registry anymore and we would return here without invoking the callback
  • if we didn't cancel we would here call remove and if now dart wants to cancel in the cancelRequest method, we can't get the request anymore as it has been removed from the registry

Comment on lines +45 to +48
let request = RemoteImageRequest(id: requestId, task: task) { result in
Self.registry.remove(requestId: requestId)
completion(result)
}
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For remote images we can just say, we always call remove together with the completion

Comment on lines +129 to +126
Self.registry.remove(requestId: requestId)?.cancel()
// For now don't call .remove here since we call completion in the handleCompletion method
Self.registry.get(requestId: requestId)?.cancel()
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removal here caused issue 1. above

Comment on lines +10 to +12
// only include if we can guarantee that for a queued operation that's
// cancelled we also call the callback with the cancelled result
// operation?.cancel()
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a operation was queued but not yet run, this would stop it from starting, and invoking our cancellation callback.

Don't calling cancel runs the block, and fixes our issue

Comment thread mobile/ios/Runner/Images/ImageRequest.swift Outdated
Comment thread mobile/ios/Runner/Images/LocalImagesImpl.swift
Comment thread mobile/ios/Runner/Images/LocalImagesImpl.swift Outdated
Comment thread mobile/ios/Runner/Images/RemoteImagesImpl.swift
Comment thread mobile/ios/Runner/Images/RemoteImagesImpl.swift Outdated
@alextran1502 alextran1502 merged commit 8a975e5 into main Apr 10, 2026
51 of 52 checks passed
@alextran1502 alextran1502 deleted the refactor/ios-image-request-base branch April 10, 2026 15:56
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request Apr 10, 2026
…4) (#139)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [immich-app/immich](https://github.com/immich-app/immich) | patch | `v2.7.3` → `v2.7.4` |

---

### Release Notes

<details>
<summary>immich-app/immich (immich-app/immich)</summary>

### [`v2.7.4`](https://github.com/immich-app/immich/releases/tag/v2.7.4)

[Compare Source](immich-app/immich@v2.7.3...v2.7.4)

### v2.7.4

This release addresses some issues with image rendering on the mobile app

#### What's Changed

##### 🐛 Bug fixes

- refactor(mobile): cleanup iOS image loading pipeline by [@&#8203;LeLunZ](https://github.com/LeLunZ) in [#&#8203;27672](immich-app/immich#27672)
- fix(server): hide original filename when not showing metadata by [@&#8203;meesfrensel](https://github.com/meesfrensel) in [#&#8203;27581](immich-app/immich#27581)
- fix(mobile): fix Flutter cache eviction on thumbnails    by [@&#8203;LeLunZ](https://github.com/LeLunZ) in [#&#8203;27663](immich-app/immich#27663)
- chore: pump cronet version by [@&#8203;shenlong-tanwen](https://github.com/shenlong-tanwen) in [#&#8203;27685](immich-app/immich#27685)

**Full Changelog**: <immich-app/immich@v2.7.3...v2.7.4>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9naXRodWItcmVsZWFzZSIsInR5cGUvcGF0Y2giXX0=-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/139
Co-authored-by: bot-owl <bot@erwanleboucher.dev>
Co-committed-by: bot-owl <bot@erwanleboucher.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants