# 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.
