RORK LABJP
BUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the outputNATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other buildersPLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screenCOMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to endPRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that backDEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verifyBUILD — Rork Max runs real Macs in the cloud loaded with Xcode and the iOS SDK, writing SwiftUI, compiling, reading the errors and building again. That loop, not the code generation, is what lifts the outputNATIVE — What comes out is pure Swift and SwiftUI, not React Native. Reaching AR, Metal graphics and widgets that React Native cannot touch is the real gap between this and other buildersPLATFORMS — Coverage spans iPhone, iPad, Apple Watch, Apple TV and Vision Pro, plus iMessage. Worth a look if you want to start from a watch app or an extension rather than a phone screenCOMPANION — The Rork Companion app lets you check a generated build on a real iPhone without a paid Apple Developer account, lowering the bar for trying a first project end to endPRICING — Free to start, paid plans from $25 a month, and Rork Max on the $200 Max plan. Worth working out up front how many projects it takes to earn that backDEADLINE — From August 31, 2026, Google Play requires target API level 36 or higher for new apps and updates alike. Ten days out, and the targetSdkVersion of what you generate is yours to verify
Articles/Dev Tools
Dev Tools/2026-08-10Advanced

Switching to Signed URLs Killed My Image Cache — Decoupling expo-image Keys from the URL

Signed URLs rewrite their query string on every expiry, so a URL-keyed cache never hits. Here is how to derive a stable key and drive expo-image's writeToCacheAsync and readFromCacheAsync yourself, with measured results.

Rork539expo-image6signed URLscache designReact Native227

Premium Article

The week after I moved image delivery behind signed URLs, one graph moved: bandwidth. Same catalog, same resolutions, same screens. The only thing that changed was the shape of the URL.

The culprit was the cache key. A signed URL rewrites X-Amz-Signature and X-Amz-Date every time the expiry rolls over. To the library, that is not yesterday's image. It is an image it has never seen.

As an indie developer running image-heavy wallpaper apps, I spent an embarrassing amount of time raising the disk cache ceiling before I understood this. Raising the ceiling does nothing. The cache was not full. It was never being hit.

Every new signature makes the same image a different image

A signed URL attaches proof to an object path: this key, this operation, valid until this moment. Because the proof is bound to an expiry, it has to be regenerated once that expiry passes.

So the same wallpaper, w042.webp, shows up like this across sessions:

Session 1: https://cdn.example.net/wallpapers/w042.webp?X-Amz-Expires=3600&X-Amz-Date=20260810T000000Z&X-Amz-Signature=6f1c...
Session 2: https://cdn.example.net/wallpapers/w042.webp?X-Amz-Expires=3600&X-Amz-Date=20260811T000000Z&X-Amz-Signature=a93e...

The path is identical. The string is not. That gap is the whole problem.

ComponentBehavior across sessionsSafe to key on?
HostUsually stable, but changes when you move CDNsNot on its own
Path (/wallpapers/w042.webp)Stable. Expresses object identityYes
X-Amz-Date / X-Amz-SignatureChanges on every expiry, by designNever
Transform params (?w=1080&fm=webp)Changes when the request changesYes. Different variant, different key

The key should encode which object, in what shape — never who authorized it, and when.

Where the default cache key actually comes from

expo-image derives its on-disk location from the source.uri string. cachePolicy selects which layers store the bytes, memory or disk. It does not change how the key is built. Conflating the two is how you end up with cachePolicy="memory-disk" set correctly and nothing being reused.

// This picks a storage layer. It has zero influence on key stability.
<Image
  source={{ uri: signedUrl }}   // the string changes every session
  cachePolicy="memory-disk"     // bytes get stored, then never looked up again
  style={styles.thumb}
/>

The bytes do land on disk. They simply become entries nobody will ever request again, quietly consuming your disk budget. The bloat side of that story is covered in Your Rork App's "Documents & Data" Keeps Growing, but when signed URLs are involved, tuning the ceiling will not help. The problem is the key, not the capacity.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
You can pinpoint why your bandwidth climbed after moving to signed URLs, and decide exactly which layer to fix
You get working code that manages expo-image cache keys yourself through writeToCacheAsync and readFromCacheAsync
You can estimate how much of your hit rate a key redesign would actually recover, using your own access distribution
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-06-25
When an Image-Heavy Rork App Quietly Bloats Its Cache and Dies on Memory — Field Notes on Measuring and Capping
In a Rork app where images are the product, expo-image's disk cache and resident memory creep up over a session and surface as OOM crashes. Here's how I measured the bloat, where I set caps, and what I trimmed on the delivery side — with working code, in the order that actually helped.
Dev Tools2026-06-19
When Rork-Built Lists Stutter: Designing Image Caching and Prefetch
A FlatList from Rork starts stuttering once the images pile up. Here is how I restore smoothness with expo-image caching, recyclingKey, prefetch, and a move to FlashList, with the device numbers I measured.
Dev Tools2026-06-12
Your Rork App's 'Documents & Data' Keeps Growing — Taming expo-image's Disk Cache
My wallpaper app's binary was 40 MB, yet 'Documents & Data' had ballooned to 2.4 GB. Here is how I diagnosed expo-image's unbounded disk cache and fixed it with cachePolicy tuning, thumbnail URLs, and generational cache clearing.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →