RORK LABJP
TEST — The Rork Companion app lets you test on a real iPhone without a paid Apple Developer accountCLOUD — Code compiles on a cloud Mac, streaming a 60fps live simulator with real touch inputBROWSER — Design, code, and test entirely in Chrome or Safari — no Xcode requiredPUBLISH — Two-click App Store publishing keeps the submission process simpleMAX — Rork Max builds native Swift apps for iPhone, iPad, Apple Watch, and Vision ProRN — Standard Rork generates iOS and Android apps together with React Native (Expo)TEST — The Rork Companion app lets you test on a real iPhone without a paid Apple Developer accountCLOUD — Code compiles on a cloud Mac, streaming a 60fps live simulator with real touch inputBROWSER — Design, code, and test entirely in Chrome or Safari — no Xcode requiredPUBLISH — Two-click App Store publishing keeps the submission process simpleMAX — Rork Max builds native Swift apps for iPhone, iPad, Apple Watch, and Vision ProRN — Standard Rork generates iOS and Android apps together with React Native (Expo)
Articles/Dev Tools
Dev Tools/2026-05-10Intermediate

When expo-image-picker won't launch in your Rork app — Info.plist and permission fixes that actually worked

Your Rork-generated app's pick image button works in the simulator but does nothing on TestFlight builds. Walk through the four real-world causes I keep hitting: missing Info.plist keys, wrong permission order, iOS 14 Limited Photo Access, and Android 13+ media permissions.

expo-image-pickerInfo.plist5permissions6iOS85Android43troubleshooting66

You tap the "pick image" button in your Rork-generated app and absolutely nothing happens — but only on TestFlight, not in the simulator. I lost a full day to that exact pattern recently.

I have been shipping individual mobile apps since 2014, mostly wallpaper-style apps that have crossed 50 million cumulative downloads. Image picker has shipped in more than ten of them, and I still get bitten by Info.plist whenever I trust the AI-generated expo-image-picker snippet without double-checking the platform configuration. The four causes below cover almost every "won't launch" report I have seen, so working through them in order tends to unblock most teams.

Cause 1: Info.plist keys are missing (the first wall on iOS)

Since iOS 10, accessing the photo library or camera requires a usage-description string in Info.plist. Apps generated by Expo or Rork often start out without those keys at all on the first build.

The signature of this failure is that the simulator works fine, but on a real device — especially via TestFlight — tapping the button does nothing or the app crashes outright.

Add the keys under ios.infoPlist in app.json.

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSPhotoLibraryUsageDescription": "We use your photo library so you can choose a profile picture.",
        "NSCameraUsageDescription": "We use the camera so you can take a profile picture.",
        "NSPhotoLibraryAddUsageDescription": "We save images you create back to your photo library."
      }
    }
  }
}

Editing the file alone does not propagate the change. With Expo Go, restart the dev server; with a native build, you have to run EAS Build again. When someone tells me "I changed app.json and nothing happened," nine times out of ten they simply have not rebuilt the binary.

Cause 2: Calling launchImageLibraryAsync without requesting permission first

A common pattern in Rork-generated code is to call launchImageLibraryAsync directly without prompting for permission. Some platforms silently return an empty result in that case, which looks exactly like a button that does nothing.

The correct order is to request permission, handle the rejection path, and only then launch the picker.

import * as ImagePicker from 'expo-image-picker';
import { Alert, Linking } from 'react-native';
 
async function pickImage() {
  // 1. Request permission first; bail out clearly on rejection.
  const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
 
  if (status !== 'granted') {
    Alert.alert(
      'Photo access not allowed',
      'Please grant photo access from the Settings app.',
      [
        { text: 'Cancel', style: 'cancel' },
        { text: 'Open Settings', onPress: () => Linking.openSettings() },
      ]
    );
    return;
  }
 
  // 2. Launch the picker after the permission is granted.
  const result = await ImagePicker.launchImageLibraryAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    allowsEditing: true,
    quality: 0.8,
  });
 
  if (!result.canceled) {
    // Expected output: result.assets[0].uri begins with file://
    console.log(result.assets[0].uri);
  }
}

The most important detail is what happens when the user denies permission. Once a user taps "Don't Allow," the OS prompt never appears again, so your app has to provide an alternative path back to Settings. Skip that and you end up with one-star reviews that simply read "the button does nothing." During the years my wallpaper apps were peaking on AdMob, I learned the cost of that oversight the hard way.

Cause 3: The iOS 14 Limited Photo Access trap

iOS 14 introduced Limited Photo Access — the option that lets the user share only selected photos. When the user picks "Selected Photos Only," your app can only see the photos they explicitly authorized.

requestMediaLibraryPermissionsAsync() returns granted even in Limited mode, so on paper everything looks fine. From the user's side, however, the picker shows almost no images, and they file a "the picker is broken" support ticket. I have shipped that bug into multiple apps before noticing.

If you want to provide a flow back to "All Photos," you need the iOS native API PHPhotoLibrary.shared().presentLimitedLibraryPicker(from:). Expo's stock module does not wrap it. For a solo developer, my pragmatic answer has been to design the experience so that Limited mode does not break the app:

  • Show a friendly "no photos selected" message when the result is empty.
  • Document the "All Photos" toggle in Settings with screenshots.
  • Recommend "All Photos" during onboarding when the feature really needs it.

For the broader question of how to design permission flows, troubleshooting permission dialogs that never appear in your Rork app is worth pairing with this article.

Cause 4: Android 13+ media permission changes

Until Android 12, READ_EXTERNAL_STORAGE was enough to read photos. Android 13 (API level 33) split that into READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, and READ_MEDIA_AUDIO.

Expo SDK 49+ ships an expo-image-picker that follows the new permissions internally, but if android.permissions in your app.json still references the old ones, you may see build-time warnings or a Play Store reviewer flagging your app for requesting unnecessary permissions.

{
  "expo": {
    "android": {
      "permissions": [
        "android.permission.READ_MEDIA_IMAGES",
        "android.permission.READ_MEDIA_VIDEO"
      ]
    }
  }
}

A subtler win on Android 13+ is the system-provided Photo Picker, which removes the need for any of those permissions altogether. expo-image-picker already routes through it for simple selections, so a basic profile-photo flow can ship with no permission request at all. If your picker "won't launch," it is also worth asking whether the runtime permission request is even running in the first place.

When you want to step back and look at the whole native build pipeline, working with Expo prebuild and custom native modules and fixing environment variable configuration errors in Rork help spot the cases where Info.plist alone is not the whole story.

One thing to do first

If only one fix fits in your day, it is this: add NSPhotoLibraryUsageDescription to ios.infoPlist in app.json, then rebuild with EAS Build. I have lost count of how many times that single move has solved the problem for me.

If this saves you a few hours of the same dead end I walked into, that is more than enough for me. Thank you for reading.

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 →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-05-04
Microphone and Audio Recording Not Working in Rork — A Symptom-Based Troubleshooting Guide
When microphone or audio recording stops working in a Rork-generated app, the root cause is often not obvious. This guide walks through common failure patterns — permissions, audio mode setup, simulator limits, and async pitfalls — with working code fixes.
Dev Tools2026-04-23
Why Your Rork App Can't Save Images to the Camera Roll — A Complete Fix
Image and video saving often breaks the moment a Rork-generated app leaves the simulator. This guide walks through the permission model, expo-media-library quirks, and Android 13+ scoped storage changes that reliably trip people up.
Dev Tools2026-05-23
expo-haptics Silent on Production Builds in Rork — Simulator, Device, and Low Power Mode Pitfalls
Your Rork-generated app taps the favorite button and nothing happens on TestFlight — but Expo Go works fine. Lessons from a wallpaper indie shop on the five most common reasons expo-haptics goes silent, with working call patterns for each.
📚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 →