MobiWeb Maintainer Help

Setting Up a New App

Checklist for turning the MobiWeb shell into a new client app, from an empty branch to store submission. Apps are published under our own accounts: Apple team D33A49X2M4 and our Google Play organization account. For a client that needs its own account, see Publishing Under Another Account.

Work top to bottom. CI and store-review steps have their own pages:

1. Collect from the client

Item

Used for

App name, at most 30 characters

Store listings, home-screen label

Web app URL for prod and dev

INITIAL_URL/RemoteContentURL

Icon source: square PNG, at least 1024×1024, no transparency for iOS

Icons and splash

Splash/brand colors, light and dark

Splash backgrounds

Privacy policy URL

Both stores (required)

Support URL and contact email

Both stores

Demo account for reviewers (login + password, no 2FA)

Both reviews: every app here starts behind a login

Which native features the web app uses: push, camera, location, Bluetooth printing, NFC, biometrics, OAuth, offline

Permissions, usage texts, store declarations

Domains for deep links, if any

Associated Domains / App Links

Decide the identifiers once and never change them: the Android applicationId and the iOS bundle ID. Existing apps use reverse-DNS like ph.ity.<app>. The two may differ, as in com.seriousmd and com.seriousmd.cloud.

2. Create the branches

git switch -c apps/<client>/<app>/dev origin/main # …configure (steps 3–5), commit, then: git switch -c apps/<client>/<app>/prod

Keep both branches. Dev is for testers (Firebase App Distribution and TestFlight). Pushing prod starts a store upload. Merge main into both branches to pick up shell updates.

3. Configure the shell

Android

File

Change

android/app/build.gradle

applicationId, versionName (versionCode can stay; CI computes it), INITIAL_URL, COOKIE_URLS, PREFS_NAME (<applicationId>.prefs), and any feature flags the app needs (OAUTH_ENABLED, DEEPLINK_ENABLED, OFFLINE_ENABLED, FILE_HOST_SUFFIXES, HEIC_HANDLING, SWIPE_REFRESH_ENABLED)

android/app/src/main/res/values/strings.xml

app_name and title_activity_main

android/app/src/main/AndroidManifest.xml

Deep-link intent-filters (host + path prefix) if the app uses App Links; remove permissions for features the app doesn't use

android/app/google-services.json

From the Firebase project (step 5)

iOS

File

Change

ios/App/App.xcodeproj/project.pbxproj

PRODUCT_BUNDLE_IDENTIFIER for App and NotificationService (<bundle>.NotificationService), in both Debug and Release; MARKETING_VERSION; per-app build settings (FILE_HOST_SUFFIXES, OAUTH_ENABLED, FACEBOOK_APP_ID, MEDIA_CAPTURE_TRUSTED_HOST_PATTERN)

ios/App/App/Info.plist

CFBundleDisplayName, RemoteContentURL (the web app URL), and specific permission texts (see below)

ios/App/App/App.entitlements

applinks:<domain> entries for deep links; leave the rest as is

ios/App/App/GoogleService-Info.plist

From the Firebase project (step 5)

Rewrite the permission texts (NS…UsageDescription) for this app. The defaults, such as "…for various purposes", are generic and App Review rejects vague texts (guideline 5.1.1). Say what the feature does in this app, for example: "Bluetooth is used to print delivery receipts on your thermal printer." Remove the keys for features the app doesn't use.

Notification sounds (optional)

Add .wav files to sounds/ on the app branch. See Notification Sounds.

4. Icons and splash screens

Generate them with @capacitor/assets, which is already a dev dependency. Put the sources in assets/ at the repo root:

File

Size

Use

assets/icon-only.png

1024×1024

Full icon (iOS and legacy Android)

assets/icon-foreground.png

1024×1024

Android adaptive icon foreground (keep the logo inside the center 66%)

assets/icon-background.png

1024×1024

Android adaptive icon background (or a solid color)

assets/splash.png/assets/splash-dark.png

2732×2732

Splash, light and dark

Or use "easy mode" with a single assets/logo.png (and optionally logo-dark.png) plus background colors:

npx capacitor-assets generate --ios --android # easy mode: npx capacitor-assets generate --ios --android \ --iconBackgroundColor '#ffffff' --iconBackgroundColorDark '#111111' \ --splashBackgroundColor '#ffffff' --splashBackgroundColorDark '#111111'

--ios --android skips the PWA icons, which this shell doesn't use. Commit the sources in assets/ together with the generated files under android/app/src/main/res/ and ios/App/App/Assets.xcassets/. iOS icons must not have transparency.

5. Firebase project

  1. Create the project in the Firebase console. Keep Google Analytics on: the app uses Analytics and Crashlytics.

  2. Add the Android app with the applicationId and download google-services.json into android/app/. Add the SHA-1 of the upload key only if the app uses Google sign-in.

  3. Add the iOS app with the bundle ID and download GoogleService-Info.plist into ios/App/App/.

  4. APNs key for iOS push: go to Project settings → Cloud Messaging → Apple app configuration and upload an APNs auth key (.p8) from developer.apple.com → Keys, with Apple Push Notifications enabled, plus its Key ID and Team ID D33A49X2M4. One APNs key works for every app in the team, so reuse it rather than creating one per app.

  5. App Distribution (dev builds): grant the CI service account access. See CI Release.

6. Push proxy (fcm-proxy)

The proxy sends through one service account (FCM_CREDENTIALS_FILE) for every Firebase project.

  1. In the new Firebase project's Google Cloud IAM, grant that service account the role Firebase Cloud Messaging API Admin, and make sure the Firebase Cloud Messaging API (v1) is enabled.

  2. In the fcm-proxy repo, add a mapping for the web team's user id to config/project-mappings.json, with project_id and, if the app uses notification actions with requests, allowed_action_origins. Restart the proxy.

  3. Generate the web team's token: npx ts-node scripts/generateUuid.ts -t -n (new user id + non-expiring token), and send it to them over a secure channel.

7. Apple Developer and App Store Connect

  1. App IDs: register <bundle> with Push Notifications, Associated Domains, Communication Notifications and Time Sensitive Notifications, and <bundle>.NotificationService with no capabilities. Critical Alerts needs a separate entitlement request to Apple, so only ask for it if the app needs it.

  2. App record: App Store Connect → Apps → + → New App: platform iOS, name, primary language, bundle ID, SKU.

  3. Internal TestFlight group with the team, so dev builds reach testers right away.

The listing itself is part of App Store Review.

8. Google Play Console

  1. Create the app: name, default language, App (not game), Free/Paid. Free/Paid can't be changed from free to paid later.

  2. Play App Signing is on by default. Our shared keystore holds the upload key for the app; Google holds the app signing key.

  3. First upload is manual: build a signed AAB once (or download one from a CI run's artifacts) and upload it to the internal testing track. Google rejects API uploads until then.

  4. Invite the CI Play service account for this app. See CI Release.

The listing and policy declarations are part of Google Play Review.

9. CI and the first builds

Follow CI Release → Onboarding an app: create the GitHub environment, add the key to the shared keystore, set the per-app secrets and variables, push dev, and start a manual run.

Smoke-test the dev builds on real devices:

  • login

  • push notifications in the foreground, in the background and while killed

  • every native feature the client named in step 1

  • deep links, if any

  • dark mode splash

The web team hosts the verification files on each domain:

  • iOS: https://<domain>/.well-known/apple-app-site-association, listing D33A49X2M4.<bundle>.

  • Android: https://<domain>/.well-known/assetlinks.json, with the app signing key SHA-256 from Play Console → App integrity (not the upload key).

Both must be served over HTTPS without redirects.

15 November 2026