DOCS LLMs

RailsFast Native Quickstart

This is the fastest path from "I have RailsFast Native access" to a real Rails server running inside both native shells.

RailsFast Native has three repos:

  • railsfast-base — the Rails app and native server contract
  • railsfast-android — the Android shell
  • railsfast-ios — the iOS shell

The native apps are not standalone demos. They render your Rails app, start at /native/entry, load path configuration from Rails, and use the same Devise session cookie as the web app.

1. Clone The Repos With The Right Remotes

Start with the normal RailsFast Base quickstart. The important remote convention is: keep the template remote named railsfast, and put your private downstream repo on origin.

git clone --origin railsfast https://github.com/railsfast/railsfast-base.git myapp
cd myapp
git remote add origin https://github.com/YOUR-USERNAME/myapp.git
git push -u origin main

Do the same for the native repos:

git clone --origin railsfast https://github.com/railsfast/railsfast-android.git myapp-android
cd myapp-android
git remote add origin https://github.com/YOUR-USERNAME/myapp-android.git
git push -u origin main

git clone --origin railsfast https://github.com/railsfast/railsfast-ios.git myapp-ios
cd myapp-ios
git remote add origin https://github.com/YOUR-USERNAME/myapp-ios.git
git push -u origin main
IMPORTANT

Keep the downstream repos private. RailsFast template repos must not be published publicly.

This remote layout is intentional. Later, you can fetch template updates from railsfast and push your product work to origin without confusing the two histories.

2. Get Rails Running First

Finish the RailsFast Base setup before debugging native:

cd myapp
bin/railsfast init
bin/dev

Confirm these URLs exist while the server is running:

curl -I http://localhost:3000/native/entry
curl -I http://localhost:3000/native/configurations/ios/v1.json
curl -I http://localhost:3000/native/configurations/android/v1.json
curl -I http://localhost:3000/.well-known/railsfast.json

If /native/entry is broken, fix Rails first. The native shells only present the server contract; they cannot repair missing routes, bad auth redirects, or invalid path configuration.

3. Run Android

Android emulator networking is different from your Mac's browser. The emulator reaches the Rails server on your Mac through http://10.0.2.2:3000, not localhost.

cd myapp-android
./gradlew installDebug

To point at another server:

./gradlew installDebug -PrailsfastBaseUrl=https://app.example.com

Make the Android app yours with the rename script:

bin/rename \
  --package com.example.myapp \
  --app-name "My App" \
  --base-url https://app.example.com \
  --commit

If you want the rename script to also update Rails' native Android package setting, pass the absolute path to your Rails config:

bin/rename \
  --package com.example.myapp \
  --app-name "My App" \
  --base-url https://app.example.com \
  --railsfast-config /absolute/path/to/myapp/config/railsfast/railsfast.yml \
  --commit

Use a real HTTPS domain before testing Android App Links. Local hosts can be opened with an explicit adb intent, but they cannot pass Android App Links verification.

4. Run iOS

The iOS project is generated from project.yml with XcodeGen. After every project.yml identity change, regenerate before opening Xcode.

cd myapp-ios
brew install xcodegen
xcodegen generate
open RailsFast.xcodeproj

For local Rails development, the simulator can use http://localhost:3000; NSAllowsLocalNetworking is already set. For staging or production, edit RAILSFAST_BASE_URL in project.yml, then regenerate.

Run the test suite on an iOS 26 simulator when you touch shell behavior:

xcodebuild test \
  -project RailsFast.xcodeproj \
  -scheme RailsFast \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.3.1' \
  CODE_SIGNING_ALLOWED=NO
TIP

If Xcode reports multiple or missing simulators for a name + OS destination, use a concrete simulator UDID instead: xcrun simctl list devices available, then pass -destination "platform=iOS Simulator,id=<UDID>". RailsFast iOS CI does this because hosted runner images change their pre-created simulator list over time.

Make the iOS app yours in project.yml:

  • app display name
  • bundle identifier
  • RAILSFAST_BASE_URL
  • RAILSFAST_ASSOCIATED_DOMAIN
  • custom URL scheme
  • Apple Team ID/signing settings when you are ready for devices and TestFlight

Before a TestFlight/App Store upload, bump the source-controlled iOS version settings from the iOS repo root:

ruby scripts/ios_release_version

Use ruby scripts/ios_release_version --bump none for another TestFlight build of the same public version. The script updates MARKETING_VERSION and CURRENT_PROJECT_VERSION in project.yml, runs xcodegen generate, and leaves the generated Xcode project ready to commit. Do not edit Version or Build only in Xcode on the signing Mac; XcodeGen will overwrite those Xcode-only edits the next time the project is regenerated.

Keep the user-agent layering intact. Your app name is app-specific; the RailsFast Native iOS / RailsFast Native Android segments are template identity and Rails uses them for native detection.

Before shipping either shell, generate the installed launcher icons and launch surfaces from the same canonical brand artwork. Follow App Icons and Launch Screens for the exact iOS asset-catalog, Android adaptive-icon, cache-reset, and cold-launch verification workflow.

Universal Links and Android App Links require both sides:

  • the native binary claims the domain
  • Rails serves the matching well-known file over real HTTPS

RailsFast Base serves:

  • /.well-known/assetlinks.json
  • /.well-known/apple-app-site-association
  • /apple-app-site-association

The endpoints intentionally fail closed until you configure real app identity. Android returns [] until a package name and SHA-256 fingerprint exist. iOS returns a valid non-claiming AASA payload until Team ID, bundle/app IDs, and path claims exist.

For iOS, configure the app IDs and path list with IOS_APP_LINK_TEAM_ID, IOS_APP_LINK_BUNDLE_ID, IOS_APP_LINK_APP_IDS, IOS_APP_LINK_PATHS, or native.ios.app_link_paths in config/railsfast/railsfast.yml.

For Android, configure the package name and release signing fingerprint before expecting Chrome-to-app auto-open to work.

Restart Rails after editing config/railsfast/railsfast.yml; it is app configuration, not hot-reloaded page content.

CAUTION

Do not claim /* by default. Claim only the paths the native app should open. Product, marketing, legal, support, billing, and auth URLs may need different behavior.

6. Verify The Real Flows

Before you call the native setup done, test these on both platforms:

  • cold launch lands on /native/entry
  • signed-out welcome screen opens
  • sign-in and sign-up open as native sheets/modals
  • bad credentials stay on the form and show the right validation message
  • successful auth goes through /native/handoff and closes the auth shell
  • logout returns to the signed-out shell
  • onboarding users stay outside tabs
  • signed-in users reach the tab shell
  • /dashboard, /native/me, /settings, /organizations, /memberships, /invitations, and /billing stay in their expected tab families
  • native toasts render for bridge-capable clients, while HTML flash remains the fallback
  • a native JSON 401 returns to auth instead of leaving the shell stuck
  • Android works with at least one emulator
  • iOS works on iOS 18 and iOS 26 if your deployment range spans both visual systems

Gotchas To Keep In Mind

  • Rails owns product rules. Native owns container behavior.
  • Path configuration is the routing source of truth; bundled native JSON files are fallbacks, not a second product contract.
  • Query strings do not decide tab ownership. Route-family matching is path-based.
  • iOS and Android share settings.shell, but their rules[] arrays are platform-specific.
  • Auth success goes to /native/handoff, not directly to /dashboard and not back through the auth sheet.
  • Bridge markup is gated by advertised bridge-component support in the user agent, not by platform name.
  • Cookie sharing solves authentication continuity, not CSRF. Native writes need an explicit server-side CSRF policy.
  • Android's emulator host is 10.0.2.2; iOS Simulator can use localhost.
  • iOS app identity is generated from project.yml; run xcodegen generate after changing it.
  • iOS release version/build numbers are generated from project.yml; use ruby scripts/ios_release_version instead of editing Xcode on the signing Mac.
  • Native billing is intentionally not enabled by default. Do not expose web pricing or checkout inside the native apps until you have a real App Store / Play billing strategy.

References

Next