Docs

iOS on the Platform

Building for iOS normally means owning a Mac: only Xcode can archive an iOS app and only Xcode can hand it to App Store Connect. On the Platform, both happen on a hosted macOS runner, so the machine you type on can be Linux, Windows, or a CI job.

The app itself is the same Hotwire Native shell as the DIY track: a thin native wrapper around your deployed site.

Before you start

  • Your app is deployed and in remote mode. iOS builds are remote mode only: a phone cannot boot a compiled Rails server. mode: remote plus an absolute https:// remote.url is required, and the Platform refuses the submission without it.
  • You are in the Apple Developer Program ($99 a year). The Platform cannot issue certificates on your behalf.
  • You have connected the CLI with every platform login.
config/everywhere.yml yaml
app:
  name: Notes
  bundle_id: com.example.notes
  version: "1.4.0"
  mode: remote

remote:
  url: https://notes.example.com

build:
  targets:
    - ios-arm64:testflight

platforms:
  ios:
    bundle_id: com.example.notes.ios   # optional; the App Store often wants its own id

Whichever bundle id iOS ends up with is the one you register with Apple and the one your provisioning profile must cover.

Upload three credentials

TestFlight and the App Store need the same three, all under Credentials → New. None of them requires a Mac.

1. Apple Distribution certificate (.p12)

If you have one, export it with its password and upload it. If you do not, and you have no Mac to create one, use the Platform's Mac-less flow: it generates the keypair and a CSR on the server, you upload the CSR to Apple Developer, and you upload Apple's issued .cer back. See credentials.

2. Provisioning profile (.mobileprovision)

Entirely web-based:

  1. Register your iOS bundle id in Apple Developer → Identifiers.
  2. In Apple Developer → Profiles, create an App Store Connect distribution profile for that bundle id, choosing the same Apple Distribution certificate you uploaded.
  3. Download the .mobileprovision and upload it. The Platform reads its name, team, app id, and expiry, and refuses an expired one or one whose team doesn't match what you entered.

3. App Store Connect API key (.p8)

In App Store Connect → Users and Access → Integrations, generate a Team Key with the Developer role or higher and download the .p8. Apple lets you download it exactly once. Upload it with its Key ID, in the key's row, and Issuer ID, at the top of that page.

This key is what the runner uses to upload your build to App Store Connect.

The Credentials page has an "iOS · TestFlight and the App Store" checklist that turns green when all three are verified.

Build it

Terminal bash
every platform build --target ios-arm64:testflight

Use ios-arm64:app_store for an App Store submission build; everything below is the same for both. With the target already in build.targets, plain every platform build picks it up.

The build waits for a hosted macOS runner, then archives, signs with your distribution certificate and profile, uploads the .ipa to App Store Connect, and exports a second copy of the .ipa into dist/ for your records. You do not install that file directly: iOS apps reach devices through App Store Connect.

Bump the version for every upload. The shell stamps a fixed build number of 1, so App Store Connect tells your uploads apart by app.version (or platforms.ios.version) alone. Re-uploading the same version is rejected on Apple's side.

After the build

  1. App Store Connect processes the upload. Usually minutes, sometimes longer. The build appears in the app record's TestFlight tab when it is done.
  2. Testers. Internal testers can install it as soon as processing finishes; external testing needs Apple's beta review first. Both are done in App Store Connect.
  3. Promote it in the dashboard. Open the succeeded build, press Promote, and pick a channel. For iOS this records the release against the version you shipped. The upload already happened on the runner, so promoting moves nothing along in App Store Connect. Submitting an app_store build for review stays a manual step there.

Promotion is refused if the build never uploaded. That means the API key was missing or wrong when the runner ran; add it and build again.

When a build is refused

  • "iOS builds are remote mode only". Set mode: remote in everywhere.yml.
  • "remote mode needs remote.url set to an absolute http(s) URL". A relative or empty remote.url. The shell has nothing to point at without it.
  • Missing credentials. The error names which of the three are missing or unverified, and links to the credentials page.
  • Unsupported target. ios-arm64:direct does not exist. iOS ships through App Store Connect, so the channel must be testflight or app_store.

What's the same as DIY

Tabs, icons, colors, native extensions, and the bridge all behave as they do on the DIY iOS page. The Platform changes where the build runs, not what gets built. Local development still uses every dev --ios on a Mac.

Rails · Hanami · Sinatra — built with Ruby