Skip to content

Register roadflared: URL scheme and handle driver-share deep links #64

@variablefate

Description

@variablefate

Goal

Make RoadFlare iOS register itself as the handler for the roadflared: URL scheme, so tapping a roadflared:npub1...?name=... link on a device with RoadFlare installed opens the app to the Add Driver flow pre-filled with that npub.

Background

  • The roadflare.app marketing site has /share/d/<npub> driver-share pages with an "Add to RoadFlare" button. Until the custom URL scheme is handled by the app, that button will be hidden (see variablefate/roadflare-site#2).
  • We're using two separate custom schemes, partitioned by recipient app:
    • roadflared:<npub> → handled by RoadFlare iOS (rider app — this issue)
    • roadflarer:<npub> → handled by the future driver-side iOS app (out of scope here)
  • This deliberately keeps the rider and driver apps' deep-link surfaces independent. Universal Links (https://roadflare.app/share/...) are tracked separately as a future enhancement in #63.

URL shape

Opaque-URI style, same shape as the existing nostr: URI the app already parses:

roadflared:<npub>[?name=<URL-encoded display name>]

Examples:

  • roadflared:npub1abc...xyz
  • roadflared:npub1abc...xyz?name=Road%20Runner

Work required

  1. Register the scheme in RoadFlare/RoadFlare/Info.plist — add a CFBundleURLTypes entry with CFBundleURLSchemes containing roadflared.
  2. Handle incoming URLs in the SwiftUI app entry point (wherever the root App / Scene is declared) via .onOpenURL { url in ... } or UIApplicationDelegateAdaptor equivalent.
  3. Reuse parsing logic — extend DriverQRCodeParser to accept roadflared: in addition to the existing nostr: prefix, or add a thin adapter that normalizes roadflared:nostr: before delegating. Consider renaming the parser if its scope becomes URI-agnostic (e.g. DriverURIParser), but check ADRs first.
  4. Route to Add Driver flow — dispatch the parsed npub + optional name into the same code path AddDriverSheet uses today for QR scan / paste input. Pre-fill the sheet and present it.
  5. Tests — extend DriverQRCodeParserTests with roadflared: cases (with and without ?name=, with invalid npub, etc.).
  6. Follow repo conventions — run gitnexus_impact on parser changes, consider writing an ADR if the URL-scheme handling introduces new public API or app-level routing patterns.

Cold-start vs warm-app

Both need to work. .onOpenURL fires in both cases with SwiftUI. If the app is cold-started via URL, make sure the Add Driver sheet can present once the root view is ready (may need to defer presentation until the main authenticated view is loaded).

Out of scope

  • roadflarer: scheme (future driver app)
  • Universal Links — tracked in #63
  • Ride-invite deep links or any non-profile-share URLs

When this ships — checklist for restoring the site button

The matching site change lives as a draft PR here: variablefate/roadflare-site#4. It is already rebased to the current tip of main, so the diff is a clean additive 10 lines (no conflict). Do NOT merge it until the steps below are all true.

  1. App Store build is live — not TestFlight, not pending review. Confirm by searching "RoadFlare" in the public App Store from a device that does NOT have the app installed, and verifying the new version number is what downloads. Builds stuck in review or only distributed to TestFlight testers will not have the scheme registered for the general public.
  2. End-to-end test on a real device. Install the live App Store build fresh (delete any existing TestFlight copy first — TestFlight and App Store builds can coexist with different signing, leading to confusing state). Then:
    • Pick a real driver npub (one the test device is not already following).
    • In any app that supports tapping custom URLs (Notes, Messages to yourself, Safari address bar), tap roadflared:npub1...xyz?name=Road%20Runner.
    • Expected: RoadFlare opens, Add Driver sheet is presented with the npub and name pre-filled, you can complete the follow flow.
    • Repeat from a cold start (force-quit the app first) to verify the .onOpenURL handler works pre-root-view.
  3. Sanity-check the URL used on the live site. Once step 2 works, visit a live /share/d/<real-npub> page on that device — BUT note the site still won't have the button until PR Ping feature to notify offline drivers #4 merges. Inspect the rendered HTML of PR Ping feature to notify offline drivers #4's branch locally (gh pr checkout 4 in roadflare-site, open 404.html in a browser) and tap the button from there. Same expected behavior.
  4. Mark roadflare-site#4 ready for review — the PR is currently draft.
    gh pr ready 4 --repo variablefate/roadflare-site
    
  5. Squash-merge (matches the repo's established pattern — see PRs Chat does not notify rider of new messages #1 and Add logout button to Edit Profile screen #6).
    gh pr merge 4 --repo variablefate/roadflare-site --squash
    
  6. Verify the deploy. GitHub Pages auto-deploys from main on merge, usually within 60s. Visit https://roadflare.app/share/d/<real-npub> and confirm: the "Add to RoadFlare" button is visible, its href is roadflared:<npub>?name=..., and tapping it from a device with the new app installed opens the Add Driver flow.
  7. Close this issue once step 6 passes.

If something is wrong with PR #4 when you go to merge

PR #4 was rebased once after PR #6 merged. If additional main commits land between now and the iOS ship date, the PR may need rebasing again:

cd roadflare-site
git fetch origin
git checkout feat/roadflared-driver-scheme
git rebase origin/main
# If conflicts, resolve by keeping PR #4's additive changes and dropping any duplicated lines
git push --force-with-lease origin feat/roadflared-driver-scheme

The PR's diff is purely additive (adds an element with a unique id and a new conditional JS block) — it should only conflict if someone else modifies the .actions container or the renderSharePage function in the same spots. In that case, prefer manual merge over --strategy ours/theirs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions