BusinessCard/Website_AppClip_Plan.md

215 lines
7.3 KiB
Markdown

# Website + App Clip Plan
## Goal
Set up a production website/domain so the BusinessCard App Clip can be invoked reliably from QR codes and links.
## Website Stack (Provided Context)
- Frontend: React 18 SPA with Vite and `BrowserRouter`
- Hosting: Vercel
- Backend endpoint: Vercel Serverless Function (`send-email.js`)
- Data-driven content from `site.json` and `apps.json`
- App slug on site: `businesscard`
## Confirmed Website Reality (`/website`)
- Domain/site: TopDog Labs (`topdoglabs.com`)
- Routing: SPA routes in `website/src/App.jsx` (`/`, `/apps`, `/apps/:slug`, `/support`, etc.)
- Current Vercel behavior: global rewrite in `website/vercel.json`:
- `/(.*)` -> `/index.html`
- Impact: this current rewrite will also catch `/.well-known/apple-app-site-association` unless you add exclusions.
## Current Project Context
- App currently builds BusinessCard App Clip URLs as:
- `https://<APPCLIP_DOMAIN>/appclip/businesscard?id=<recordName>`
- Domain is configured in `BusinessCard/Configuration/Base.xcconfig`:
- `APPCLIP_DOMAIN = topdoglabs.com`
- App and App Clip entitlements already reference:
- `appclips:$(APPCLIP_DOMAIN)`
## What the Website Must Provide
1. A real HTTPS domain you control.
2. Apple App Site Association (AASA) file at:
- `https://<your-domain>/.well-known/apple-app-site-association`
3. Correct AASA delivery rules:
- No `.json` extension
- `Content-Type: application/json`
- Publicly accessible (no auth)
- No redirects
4. Optional fallback page/endpoint for non-iOS users.
## No Required Language
- There is no required backend language for App Clips.
- You can use static hosting only, as long as AASA is served correctly.
- Use backend/worker only if you want advanced behavior (analytics, non-iOS vCard download, redirects, etc.).
## Does Your Website `businesscard` Slug Matter?
- Not for App Clip verification itself.
- App Clip invocation for this iOS project uses `/appclip/businesscard?id=...`, not `/apps/:slug`.
- Keep `businesscard` slug for your public app page and marketing content.
- Ensure `/appclip/businesscard` and `/.well-known/apple-app-site-association` are not intercepted by SPA rewrites.
## AASA File (Starter)
Create this file on your website:
- Path: `.well-known/apple-app-site-association`
Use this template (replace values):
```json
{
"appclips": {
"apps": [
"TEAM_ID.com.mbrucedogs.BusinessCard.Clip"
]
}
}
```
Notes:
- `TEAM_ID` must match your Apple Developer Team ID.
- Bundle ID must match your App Clip target bundle ID exactly.
## Vercel + React SPA Implementation
When you clone your website repo into this workspace, apply these steps.
1. Add AASA as a static public file:
- `public/.well-known/apple-app-site-association`
2. Ensure your SPA fallback rewrite does not override AASA:
- Do not rewrite `/.well-known/*` to `index.html`
3. Ensure `/appclip/businesscard` is routed intentionally:
- Either serve a dedicated fallback page
- Or allow a specific serverless/edge handler for non-iOS behavior
4. Set explicit response header for AASA:
- `Content-Type: application/json`
### Example `vercel.json` additions
Adjust to merge with your existing config:
```json
{
"cleanUrls": true,
"headers": [
{
"source": "/.well-known/apple-app-site-association",
"headers": [
{ "key": "Content-Type", "value": "application/json" }
]
}
],
"rewrites": [
{ "source": "/.well-known/(.*)", "destination": "/.well-known/$1" },
{ "source": "/appclip/businesscard", "destination": "/appclip.html" },
{ "source": "/appclip", "destination": "/appclip.html" },
{ "source": "/api/(.*)", "destination": "/api/$1" },
{ "source": "/(.*)", "destination": "/index.html" }
]
}
```
Notes:
- Keep `/.well-known/*` before catch-all SPA rewrite.
- Keep `/api/*` callable (your contact form endpoint).
- If you want `/appclip/businesscard` to show a fallback page, add `website/public/appclip.html`.
## Minimal Files To Add in `website/public`
1. `website/public/.well-known/apple-app-site-association`
2. `website/public/appclip.html` (optional but recommended fallback)
`appclip.html` can be minimal:
- Message for iPhone users to open via camera/App Clip card
- Link for non-iOS users (for example to app page `/apps/businesscard` or App Store URL)
- Optional JS to parse `id` query parameter and show it for support/debug
## Multi-App App Clip Pattern (Recommended)
For multiple apps, use one shared domain and namespaced paths:
- BusinessCard: `/appclip/businesscard?id=<id>`
- Rituals: `/appclip/rituals?id=<id>`
- SelfieCam: `/appclip/selfie-cam?id=<id>`
Keep one AASA file and include all App Clip bundle IDs:
```json
{
"appclips": {
"apps": [
"TEAM_ID.com.mbrucedogs.BusinessCard.Clip",
"TEAM_ID.com.mbrucedogs.Rituals.Clip",
"TEAM_ID.com.mbrucedogs.SelfieCam.Clip"
]
}
}
```
Then add one rewrite per App Clip path in `vercel.json`.
## Domain + Hosting Checklist
1. Pick subdomain (recommended): `cards.yourdomain.com`
2. Configure DNS record to your host.
3. Ensure valid TLS certificate (HTTPS).
4. Add/upload AASA file at `/.well-known/apple-app-site-association`.
5. Verify no redirect occurs for that file.
## App Project Changes Required
1. Update domain in `BusinessCard/Configuration/Base.xcconfig`:
- `APPCLIP_DOMAIN = cards.yourdomain.com` (recommended)
- or use an existing domain if preferred (for example a subpath on `topdoglabs.com`)
2. Confirm entitlements still resolve correctly:
- iOS app: `BusinessCard/BusinessCard.entitlements`
- App Clip: `BusinessCardClip/BusinessCardClip.entitlements`
3. Clean and rebuild in Xcode after config changes.
Note:
- A dedicated subdomain (like `cards.topdoglabs.com`) is usually cleaner for App Clip + future deep link infrastructure.
## App Store Connect Steps
1. Open your app in App Store Connect.
2. Create/configure App Clip Experience.
3. Add invocation URL using your real domain/path (for BusinessCard:
`/appclip/businesscard`).
4. Submit for review with App Clip metadata.
## Verification Steps (Before Release)
1. Browser check:
- Open `https://<your-domain>/.well-known/apple-app-site-association`
- Confirm file returns directly (no redirect/auth).
2. Header check (terminal):
```bash
curl -I https://<your-domain>/.well-known/apple-app-site-association
```
Confirm:
- `HTTP 200`
- `content-type: application/json`
3. Device test:
- Install app + App Clip build on physical iPhone.
- Generate App Clip QR in app share screen.
- Scan QR and confirm App Clip opens.
- Confirm App Clip can fetch CloudKit card and save contact.
4. Confirm SPA still works:
- `/`
- `/apps`
- `/apps/businesscard`
- `/support`
- `/.well-known/apple-app-site-association`
- `/appclip/businesscard?id=test`
## Optional Fallback (Recommended)
For Android/desktop scans:
- Host a simple web page or download flow at `/appclip/businesscard`.
- Show contact preview or provide `.vcf` download if desired.
## Suggested Rollout Order
1. Domain + AASA live
2. Update `APPCLIP_DOMAIN`
3. Rebuild + physical-device testing
4. App Store Connect App Clip Experience
5. Release
## Risks / Gotchas
- Wrong Team ID or bundle ID in AASA breaks invocation.
- Redirecting `/.well-known/apple-app-site-association` breaks verification.
- Simulator is unreliable for full App Clip invocation testing.
- Keep CloudKit schema and public read permissions aligned with App Clip fetch flow.