7.3 KiB
7.3 KiB
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.jsonandapps.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-associationunless 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
- A real HTTPS domain you control.
- Apple App Site Association (AASA) file at:
https://<your-domain>/.well-known/apple-app-site-association
- Correct AASA delivery rules:
- No
.jsonextension Content-Type: application/json- Publicly accessible (no auth)
- No redirects
- No
- 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
businesscardslug for your public app page and marketing content. - Ensure
/appclip/businesscardand/.well-known/apple-app-site-associationare 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):
{
"appclips": {
"apps": [
"TEAM_ID.com.mbrucedogs.BusinessCard.Clip"
]
}
}
Notes:
TEAM_IDmust 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.
- Add AASA as a static public file:
public/.well-known/apple-app-site-association
- Ensure your SPA fallback rewrite does not override AASA:
- Do not rewrite
/.well-known/*toindex.html
- Do not rewrite
- Ensure
/appclip/businesscardis routed intentionally:- Either serve a dedicated fallback page
- Or allow a specific serverless/edge handler for non-iOS behavior
- Set explicit response header for AASA:
Content-Type: application/json
Example vercel.json additions
Adjust to merge with your existing config:
{
"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/businesscardto show a fallback page, addwebsite/public/appclip.html.
Minimal Files To Add in website/public
website/public/.well-known/apple-app-site-associationwebsite/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/businesscardor App Store URL) - Optional JS to parse
idquery 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:
{
"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
- Pick subdomain (recommended):
cards.yourdomain.com - Configure DNS record to your host.
- Ensure valid TLS certificate (HTTPS).
- Add/upload AASA file at
/.well-known/apple-app-site-association. - Verify no redirect occurs for that file.
App Project Changes Required
- 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)
- Confirm entitlements still resolve correctly:
- iOS app:
BusinessCard/BusinessCard.entitlements - App Clip:
BusinessCardClip/BusinessCardClip.entitlements
- iOS app:
- 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
- Open your app in App Store Connect.
- Create/configure App Clip Experience.
- Add invocation URL using your real domain/path (for BusinessCard:
/appclip/businesscard). - Submit for review with App Clip metadata.
Verification Steps (Before Release)
- Browser check:
- Open
https://<your-domain>/.well-known/apple-app-site-association - Confirm file returns directly (no redirect/auth).
- Open
- Header check (terminal):
curl -I https://<your-domain>/.well-known/apple-app-site-association
Confirm:
HTTP 200content-type: application/json
- 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.
- 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
.vcfdownload if desired.
Suggested Rollout Order
- Domain + AASA live
- Update
APPCLIP_DOMAIN - Rebuild + physical-device testing
- App Store Connect App Clip Experience
- Release
Risks / Gotchas
- Wrong Team ID or bundle ID in AASA breaks invocation.
- Redirecting
/.well-known/apple-app-site-associationbreaks verification. - Simulator is unreliable for full App Clip invocation testing.
- Keep CloudKit schema and public read permissions aligned with App Clip fetch flow.