RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-05-18Intermediate

Fixing iOS-Only Linking.canOpenURL False Returns in Rork — The LSApplicationQueriesSchemes Trap

Production iOS builds silently route every user to the mobile web instead of opening TikTok or X? This is almost always a missing LSApplicationQueriesSchemes entry. Here is the full fix path for Rork.

Rork515React Native209iOS109LinkingInfo.plist5Troubleshooting38

The morning after I shipped a "follow us on TikTok" and "share on X" pair of buttons into one of my wallpaper apps, three different reviewers wrote to say nothing happened when they tapped them on iPhone. The buttons worked perfectly in the simulator and on my Development Build, so it took me half a day to narrow it down to one thing: my Info.plist did not declare a single entry under LSApplicationQueriesSchemes.

I have been an indie developer since 2014, mostly shipping wallpaper and well-being apps that I keep alive with AdMob revenue, and Apple's quiet 2015 change to canOpenURL is still one of the most underrated traps in cross-platform development. The fix in Rork (and Expo / React Native in general) lives in your app config, not in your component code.

Recognize the symptoms before chasing the wrong fix

LSApplicationQueriesSchemes problems look like a lot of other Linking bugs, but the signature is specific:

  • The buttons work fine in the simulator and in your Development Build, and only break in TestFlight or App Store builds.
  • canOpenURL("tiktok://...") resolves to false even though TikTok is installed.
  • Xcode console shows -canOpenURL: failed for URL: "..." - error: "This app is not allowed to query for scheme tiktok".
  • The exact same code on Android works correctly.

When you see that fourth bullet — Xcode's explicit "not allowed to query" warning — the diagnosis is done. Since iOS 9, canOpenURL returns false for any scheme you did not declare ahead of time. Apple introduced this as a privacy boundary, because apps were using canOpenURL to silently fingerprint which other apps were installed.

The subtle damage is that openURL itself often still works, but most well-written Rork code path-branches on canOpenURL. The user has TikTok installed, your code thinks they do not, and they end up bounced into Safari every single time. That is a worse experience than not adding the button at all.

Step 1: Add LSApplicationQueriesSchemes to app.json

Rork inherits Expo's app config, so you never have to touch the native Info.plist directly. Edit app.json (or app.config.ts) at your project root:

{
  "expo": {
    "name": "MyApp",
    "ios": {
      "bundleIdentifier": "net.dolice.myapp",
      "infoPlist": {
        "LSApplicationQueriesSchemes": [
          "tiktok",
          "twitter",
          "instagram",
          "line",
          "fb",
          "fb-messenger",
          "youtube",
          "mailto",
          "tel",
          "sms"
        ]
      }
    }
  }
}

If your project uses app.config.ts:

import type { ExpoConfig } from "expo/config";
 
const config: ExpoConfig = {
  name: "MyApp",
  slug: "myapp",
  ios: {
    bundleIdentifier: "net.dolice.myapp",
    infoPlist: {
      LSApplicationQueriesSchemes: [
        "tiktok",
        "twitter",
        "instagram",
        "line",
        "fb",
        "fb-messenger",
        "youtube",
        "mailto",
        "tel",
        "sms",
      ],
    },
  },
};
 
export default config;

mailto, tel, and sms are technically allowed by default, but I list them anyway because it makes the manifest easier to audit during App Store review prep.

Step 2: Use the right scheme — app name and scheme rarely match

A common stumble: the scheme almost never matches the rebranded app name. To open X (the app formerly known as Twitter), you still need "twitter", not "x". For LINE's "add friend" flow, a Universal Link such as https://line.me/R/ti/p/... is now safer than the line:// scheme.

Reliable scheme reference for the apps that come up most often in Japanese and global indie work:

  • TikTok app: tiktok
  • X (formerly Twitter): twitter
  • Instagram profile: instagram
  • LINE "add friend" (prefer Universal Link over line://)
  • Facebook page: fb
  • Facebook Messenger: fb-messenger
  • YouTube: youtube or vnd.youtube
  • Google Maps: comgooglemaps
  • Apple Maps: maps
  • Spotify: spotify
  • Slack: slack

For anything not on that list, the fastest path is the vendor's own developer docs. Third-party scheme lists tend to be stale, so I always confirm with a real device call to canOpenURL before shipping.

Step 3: Re-run EAS Build — config changes do not hot-reload

This is where most teams lose another half day. Changing app.json does not apply to your existing Development Build, Expo Go session, or TestFlight artifact. infoPlist keys are baked into the native build, so you must rebuild at least once:

# Rebuild the development client
eas build --profile development --platform ios
 
# Rebuild the production artifact
eas build --profile production --platform ios
 
# Or local prebuild + direct device install
npx expo prebuild --clean
npx expo run:ios --device

expo prebuild --clean regenerates the entire ios/ directory, so back up anything custom you have under ios/ before running it. I personally let CI handle this and just push the app.json change — typically the new iOS build is ready about 15 minutes later.

Step 4: Wrap canOpenURL in a safe fallback

Even with the declaration in place, you still want the calling code to degrade gracefully. Schemes get retired and Apple's behaviour shifts; without a fallback, a silent regression can ship before you notice:

// utils/openExternalApp.ts
import { Linking } from "react-native";
 
type AppLink = {
  /** Deep link or universal link to the native app */
  appUrl: string;
  /** Web fallback when the app is unavailable or the scheme is rejected */
  webUrl: string;
};
 
export async function openExternalApp({ appUrl, webUrl }: AppLink): Promise<void> {
  try {
    const supported = await Linking.canOpenURL(appUrl);
    if (supported) {
      await Linking.openURL(appUrl);
      return;
    }
  } catch (e) {
    // iOS can throw when canOpenURL is called for an undeclared scheme.
    // Log it in dev, but always fall back to the web URL in production
    // so the user is never stuck on a dead button.
    if (__DEV__) {
      console.warn("[openExternalApp] canOpenURL threw", e);
    }
  }
  await Linking.openURL(webUrl);
}
 
// Example
// await openExternalApp({
//   appUrl: "tiktok://user?username=dolice",
//   webUrl: "https://www.tiktok.com/@masaki.hirokawa",
// });

The crucial detail is the outer try/catch. canOpenURL can actually throw on undeclared schemes, and without the catch you will only learn about it from a crash report after release.

Step 5: Know the 50-entry ceiling

Apple caps LSApplicationQueriesSchemes at 50 entries. Going beyond does not get you rejected during review — entries past the 50th are silently ignored. That sounds generous, but if you start covering social, maps, payments, ride-share, and dating apps in the same project, you can blow past the ceiling faster than you expect.

I keep a tiny predeploy check in package.json to catch this before App Store Connect ever sees the build:

// scripts/check-ios-queries.mjs
import fs from "node:fs";
 
const cfg = JSON.parse(fs.readFileSync("app.json", "utf8"));
const list = cfg?.expo?.ios?.infoPlist?.LSApplicationQueriesSchemes ?? [];
 
if (list.length > 50) {
  console.error(
    `LSApplicationQueriesSchemes has ${list.length} entries (Apple limit is 50).`
  );
  process.exit(1);
}
 
const dup = list.filter((x, i) => list.indexOf(x) !== i);
if (dup.length > 0) {
  console.error(`Duplicate scheme(s) detected: ${dup.join(", ")}`);
  process.exit(1);
}
 
console.log(`OK: ${list.length} scheme(s).`);

That predeploy guard has saved me from two near-submission incidents already.

When canOpenURL still returns false, check these three things

If everything above is in place and the result is still false, walk down this list in order:

  1. Stale build cache. Wipe Xcode's DerivedData, or run expo prebuild --clean to regenerate ios/. On Rork, rebuild EAS with the cache-clearing flag enabled.
  2. Local network entitlement mix-up. iOS 14's Local Network prompt is a different system from custom URL schemes. Resist the temptation to start touching NSAppTransportSecurity until you confirm LSApplicationQueriesSchemes is correctly declared.
  3. The wrong build is live on App Store Connect. Double-check the build number that TestFlight Internal Testing is actually serving. I once spent half a day debugging a "fixed" app that turned out to be the old binary being promoted untouched.

What to do next

Once Linking.canOpenURL behaves predictably, a lot of growth-oriented experiences become safe to ship — campaign-tracked deep links, conditional onboarding handoffs, app-to-app referrals. Today, open the app.json of your live Rork project and audit every external scheme your buttons can fire. If even one is missing from LSApplicationQueriesSchemes, your iOS users have been silently bouncing to the web all along.

I hope this saves you from the same half-day I spent. 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-21
Rork iOS App Rejected with ITMS-90683 on TestFlight — How to Fix Missing Purpose Strings via app.json
If your Rork-built iOS app passes upload but gets an email titled ITMS-90683: Missing Purpose String in Info.plist, this guide walks through the real cause and the permanent fix via app.json, based on 12 years of shipping personal iOS apps with the same problem appearing across new SDK updates.
Dev Tools2026-05-02
Why StatusBar Colors Won't Apply in Rork — and How to Fix It
A practical, case-by-case guide to fixing StatusBar color and style issues in Rork apps across iOS and Android, based on real shipping experience.
Dev Tools2026-04-24
Rork WebView Is Blank or Won't Load: 6 Causes to Check Before You Ship
Dropped a WebView into your Rork app and got a silent blank screen on device? Here's the checklist I run through, in the order that catches the most bugs first.
📚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 →