···36363737Then re-run `just generate` before building.
38383939+Pass your device UDID directly or via an env var:
4040+4141+```bash
4242+just device 00000000-0000000000000000 # explicit UDID
4343+just device $iphonemax # via shell env var
4444+```
4545+4646+Find your device UDID with `xcrun devicectl list devices`.
4747+3948## Commands
40494150```bash
···4857just lint-fix # Fix lint violations
4958just release # Bump build, archive, upload to App Store Connect
5059```
6060+6161+> **Note:** The Xcode project is generated from `project.yml` — run `just generate` after adding or removing Swift files, or after pulling changes that touch `project.yml`.
+11
Grain/Utilities/StoryImageCache.swift
···11+import Foundation
22+import Nuke
33+44+/// Returns `true` if the fullsize image for `story` is already in `pipeline`'s memory cache.
55+///
66+/// Extracted from `StoryViewer` so it can be tested without spinning up a SwiftUI view
77+/// and without touching `ImagePipeline.shared`.
88+func storyFullsizeCached(_ story: GrainStory?, in pipeline: ImagePipeline = .shared) -> Bool {
99+ guard let url = story.flatMap({ URL(string: $0.fullsize) }) else { return false }
1010+ return pipeline.cache.cachedImage(for: ImageRequest(url: url)) != nil
1111+}