Files
Sravan 0a2c7376b9 feat(share): "Biz Connect" in the iOS share sheet — share a photo/file into a chat
Adds the reverse direction: share FROM Photos/Files/Safari INTO a Biz Connect
conversation. An app can only appear in the iOS share sheet as an app-extension
target, so this is real native work, not a web change.

Pieces:
- mobile/ios-share/ShareViewController.swift: a UI-less Share Extension. It stages
  the shared items into the App Group container and opens bizconnect://share. It
  deliberately does NOT reimplement the chat picker — that lives in the app, which
  already has the chat list, search and upload progress. Appends to the manifest
  (never overwrites), so sharing twice before opening the app loses nothing.
- mobile/scripts/add-share-extension.rb: injects the extension target into the
  Capacitor-generated Xcode project on every CI build (Codemagic checks out fresh),
  using the xcodeproj gem that ships with CocoaPods. Embeds it, sets the bundle id
  <app>.share, and MERGES the App Group into the app's entitlements rather than
  clobbering them (push's aps-environment must survive). Idempotent.
- mobile/plugins/share-inbox: getPending()/clear() to read that manifest — the App
  Group container isn't one of Filesystem's known directories, so it needs a bridge.
- home.html: on bizconnect://share (and every resume, and cold-launch), read the
  inbox and show a "Send to…" picker over the chat list; chosen files run the SAME
  upload + /api/messages send as an in-app attachment. Reuses convertFileSrc to read
  the staged bytes with no base64 marshalling.
- ios-patch.sh registers the bizconnect URL scheme; codemagic.yaml fetches a profile
  for the .share bundle id too.

One-time manual gate (CI cannot toggle App capabilities): the App Group
group.com.bizgaze.connect must be created and enabled on both App IDs in the Apple
portal — documented in mobile/IOS_SETUP.md. Without it the two processes can't see
each other's files and sharing silently no-ops; everything else still works.

Validated cross-file: pod-name/jsName/method wiring for all three plugins, App
Group id identical in all 4 files, URL scheme consistent across extension/plist/web,
entitlement-merge preserves push. Needs a new iOS build (new targets + plugins).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 22:58:08 +05:30

118 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Biz Connect — iOS App Store setup (Codemagic, no Mac needed)
The iOS app is a Capacitor shell that loads the live Connect web UI (`https://remote.bizgaze.com`).
Building/signing/uploading happens on **Codemagic's macOS cloud** — you never need a Mac.
Bundle id: **`com.bizgaze.connect`** · CI config: [`codemagic.yaml`](../codemagic.yaml) (repo root).
---
## Step 0 — Register the App ID (Identifiers → + → App IDs → App)
On the **Register an App ID** page, only three fields matter — leave everything else default:
- **Platform**: leave as-is (the default `iOS, iPadOS, macOS…` combined App ID is fine).
- **Description**: `Biz Connect` (label only; no `@ & * "`).
- **Bundle ID**: keep **Explicit**`com.bizgaze.connect`.
- **Capabilities**: tick **Push Notifications** only. Leave all others unchecked. (Camera/mic are NOT
here — they're Info.plist runtime strings, added by the build pipeline.)
- Continue → Register. *(The App ID Prefix shown is your Team ID — note it for Step 5's APNs.)*
## Step 1 — App Store Connect: create the app record
1. [appstoreconnect.apple.com](https://appstoreconnect.apple.com) → **Apps → +****New App**.
2. Platform **iOS**, Name **Biz Connect**, primary language, **Bundle ID** = `com.bizgaze.connect`
(the App ID you registered in Step 0 now appears in the dropdown).
3. SKU: anything unique (e.g. `bizconnect-ios`). Create.
## Step 2 — App Store Connect API key (for Codemagic to sign + upload)
1. App Store Connect → **Users and Access → Integrations → App Store Connect API****+**.
2. Access **App Manager**. Generate. Note the **Issuer ID** (top of the page) and the key's **Key ID**,
and **download the `.p8`** (you can only download it once).
## Step 3 — Codemagic: connect + add the key
1. [codemagic.io](https://codemagic.io) → sign in with the git provider → add this repository.
2. **Teams → Integrations → App Store Connect → Connect**, upload the `.p8`, paste the **Issuer ID** and
**Key ID**. **Name it exactly `BizGaze App Store Connect`** (the `codemagic.yaml` references that name).
3. Codemagic detects `codemagic.yaml`. That's all the signing setup — automatic signing creates the
distribution certificate + provisioning profile from this key on the first build.
## Step 4 — Run the build
- Codemagic → the app → **Start new build** → workflow **"Biz Connect iOS → TestFlight"**.
- ~1015 min. On success the build appears in **App Store Connect → TestFlight**.
- Add yourself under **TestFlight → Internal Testing** to install via the TestFlight app on your iPhone.
## Step 5 — Push notifications (APNs) — do this once, then tell me
So the app gets **calls/messages while it's closed**:
1. developer.apple.com → **Keys → +** → enable **Apple Push Notifications service (APNs)** → download the
**`.p8`**. Note its **Key ID** and your **Team ID** (top-right of the developer portal).
2. **Send me**: the `.p8` contents, the **Key ID**, and the **Team ID**. I set these in the server `.env`
(server-side only, like the LiveKit/Giphy keys):
```
APNS_KEY=<contents of the .p8>
APNS_KEY_ID=<key id>
APNS_TEAM_ID=<team id>
APNS_BUNDLE_ID=com.bizgaze.connect
APNS_PRODUCTION=1
```
The APNs sender is already built into the server — it's a no-op until these are set.
## Step 6 — Public App Store submission (when you're ready to leave TestFlight)
In App Store Connect, fill the listing: **screenshots** (6.7" + 6.1" iPhone), description, keywords,
support URL, and a **Privacy Policy URL** (required). Complete the **App Privacy** questionnaire (we
collect account info + usage for chat/calls). Then submit for review (or flip `submit_to_app_store` in
`codemagic.yaml`).
---
### App Review note (Guideline 4.2 — "Minimum Functionality")
Apple scrutinises apps that look like "just a website". Ours passes because it ships **real native
capabilities** — push notifications, camera/microphone for calls, photo sharing. Make sure push (Step 5)
is live before the **public** submission, and in the reviewer notes mention the **native video/voice
calling + push notifications**. Do **not** advertise "share your screen" as an iOS feature in the store
listing yet — see the follow-up below (you can still *view* a screen someone else shares).
---
## Known iOS limitations & follow-ups (phase 2 — after TestFlight)
### 1. Sharing YOUR iOS screen into a meeting → needs a ReplayKit Broadcast Upload Extension
- **Why:** the app's screen share uses the web `getDisplayMedia` API, which **iOS WebViews and Safari do
not support**. Apple only allows capturing the *device* screen via **ReplayKit**.
- **What works today on iOS:** *viewing* a screen another participant shares (it's just incoming video),
chat, voice/video calls, camera, photo sharing.
- **What's needed to broadcast the iOS screen:** a native **Broadcast Upload Extension** target that
captures frames via ReplayKit and feeds them into the LiveKit/WebRTC session, plus the **App Groups**
capability (to pass data between the app and the extension). This is native Swift work — NOT part of
the Capacitor wrapper — so it's tracked as a separate task, done after the app is on TestFlight.
- **Store impact:** don't claim iOS screen-sharing in the listing until this ships, or a reviewer may
test it and it will fail.
### 2. Native mobile audio routing (speaker / earpiece / Bluetooth) — needs a Capacitor audio plugin
- Mobile **web** can't switch the audio output route (`setSinkId` is unimplemented on iOS/Android), so the
in-meeting speaker/earpiece/Bluetooth control is web-only where it works and hidden where it doesn't.
- True routing on iOS needs a small native plugin driving `AVAudioSession`. Phase-2 native task.
---
## Share Extension ("Biz Connect" in the iOS share sheet) — one-time Apple portal setup
The app now has a **Share Extension** target (`com.bizgaze.connect.share`) so users can share a photo /
video / file FROM the Photos or Files app INTO a Biz Connect conversation. The Codemagic build injects the
target and fetches a profile for it automatically, but two things can ONLY be done once, by hand, in the
Apple Developer portal — CI cannot toggle App capabilities:
1. **Create the App Group** (developer.apple.com → Identifiers → App Groups → +):
identifier **`group.com.bizgaze.connect`**.
2. **Enable the App Groups capability on BOTH App IDs** and assign them to that group:
- `com.bizgaze.connect` (the app)
- `com.bizgaze.connect.share` (the extension — create this App ID if the first build hasn't yet;
`fetch-signing-files --create` will register it, then edit it to add App Groups)
After enabling the capability, the provisioning profiles must be regenerated — the next Codemagic build
does that via `fetch-signing-files`, so just re-run it once the capability is on.
If the App Group isn't set up, the app and the extension can't see each other's files: sharing will appear
to do nothing (the extension stages the file, but the app finds an empty inbox). Everything else — download
to the Files folder, the Photos "Connect" album, Manage storage — works without it.
Also enabled by this change (main app Info.plist, done automatically by `ios-patch.sh`):
- `UIFileSharingEnabled` + `LSSupportsOpeningDocumentsInPlace` → the **Biz Connect** folder in Files.
- `CFBundleURLTypes` scheme **`bizconnect`** → lets the extension bounce back into the app after staging.