Flitzdocs
Loader plugins

URL scheme

How flitz:// links open bundles, what the QR scanner accepts, and how to use a custom scheme.

The loader opens bundles through two URL forms. flitz publish emits the download form (as a QR code in the terminal, a --qr-output PNG, and on the bundle's landing page); the run form is for bundles already on the device.

URLBehavior
flitz://download?url=<https-url>Downloads the bundle at <https-url> over HTTPS and runs it. The value must be https:// and is URL-encoded inside the url query parameter.
flitz://run?path=<path>Runs a bundle already on the device. See the platform difference below.

flitz://download

The url query parameter must be present, non-empty, and parse as an https:// URL with a host. Plain http:// is rejected. The download follows up to five redirects, each of which must also be https://. Progress is shown in the loader's download screen; a failed download shows the HTTP status and a retry action.

flitz://download?url=https%3A%2F%2Fexample.com%2Fbuilds%2Fmy_app.flitz

flitz://run

The path query parameter must be present and non-empty. What it means differs by platform:

  • Android: an absolute filesystem path the app process can read, for example a file pushed with adb push to /data/local/tmp/. Readability is checked before extraction; an unreadable path shows "Bundle file not found".
  • iOS: a bare filename inside the app's Documents directory. A value containing /, or equal to . or .., is rejected as path must be a leaf filename in Documents; a filename that does not exist there shows "Bundle file not found".

Recognition rules

The loader identifies its URLs by authority (run or download), never by scheme. The operating system only delivers URLs for a scheme your app has registered, so the scheme is already validated by the time the loader sees it — which is what makes custom schemes work with no code change.

  • Authority must be exactly run or download (case-insensitive). On iOS, a URL with any other authority is not claimed by the loader and falls through to your own open-URL handling. On Android, BundleLoaderActivity treats it as a plain launch and opens the scanner.
  • A URL whose authority is run or download but whose parameters are malformed is "ours, but invalid" and shows the loader's native error screen ("Invalid bundle URL" on Android, "Invalid URL" on iOS) with the parser's reason.
  • A new acquisition URL arriving while a bundle is already running tears the current bundle down (removing its extracted files) and opens the new one. Android relies on BundleLoaderActivity's singleTask launch mode; iOS dismisses the current runner and presents a fresh one. One bundle runs at a time.

QR scanner payloads

The loader's built-in scanner accepts two payload shapes:

PayloadBehavior
A run / download URL in your registered schemeRouted through the same parser as a deeplink. Invalid parameters show a transient error.
A bare https:// URLTreated as download?url=<scanned-url>.

Anything else — another authority, http://, plain text, a malformed URL — is rejected with an "Unsupported QR code" toast and the scanner stays active.

Custom schemes

flitz is the default scheme, not a requirement. The scheme is set through the native registration you already write; the loader has no runtime setting for it.

  • Android: set it in the Gradle plugin's block. The plugin feeds the value into the flitzUrlScheme manifest placeholder of every active variant, which resolves the AAR's <data android:scheme="${flitzUrlScheme}"> at merge time. The value must be a valid URL scheme (letter-led; letters, digits, +, -, .); the build fails otherwise.

    android/app/build.gradle.kts
    flitz {
        enabledFlavors = listOf("withLoader")
        urlScheme = "acme"
    }
  • iOS: change the single value under CFBundleURLTypesCFBundleURLSchemes in Info.plist from flitz to your scheme. No other key and no Swift change is needed.

  • Publishing: flitz publish --schema acme makes the emitted QR codes and links use your scheme. The flag is validated as a syntactically valid scheme but not checked against any app; if it does not match what the host registered, the link opens nothing. See flitz publish.

Because the loader only claims the run and download authorities, your app may share the same scheme for its own deeplinks (for example acme://profile/42): on iOS those pass through to the running bundle engine — plugin handlers and the framework navigation channel — and on Android they go to whichever activity your own intent filters name. The only reserved authorities are run and download; if you need those two words as hosts of your own deeplinks, give the loader a distinct scheme.

On this page