diff --git a/Agents.md b/Agents.md index 0404c5d..a076a99 100644 --- a/Agents.md +++ b/Agents.md @@ -203,6 +203,138 @@ Button("Save") { state.save() } - Avoid UIKit colors in SwiftUI code. +## watchOS Development (CRITICAL) + +**Read this entire section before implementing any watch functionality.** + +### Creating a Watch Target + +When adding a watchOS target to an existing iOS app: + +1. **File → New → Target → "Watch App for watchOS"** +2. Choose **"Watch App for Existing iOS App"** (NOT standalone) +3. Name it appropriately (e.g., `AppNameWatch`) +4. Xcode creates a folder like `AppNameWatch Watch App/` + +### CRITICAL: Embedding the Watch App + +⚠️ **THIS IS THE #1 CAUSE OF "WATCH APP NOT INSTALLED" ERRORS** ⚠️ + +The watch app MUST be embedded in the iOS app for deployment to real devices: + +1. Select the **iOS target** in Xcode +2. Go to **Build Phases** tab +3. Verify there's an **"Embed Watch Content"** phase +4. **CRITICAL**: Ensure **"Code Sign On Copy"** is CHECKED ✓ + +If "Embed Watch Content" doesn't exist: +1. Click **"+"** → **"New Copy Files Phase"** +2. Rename to **"Embed Watch Content"** +3. Set **Destination** to **"Products Directory"** +4. Set **Subpath** to `$(CONTENTS_FOLDER_PATH)/Watch` +5. Add the watch app (e.g., `AppNameWatch Watch App.app`) +6. **CHECK "Code Sign On Copy"** ← This is critical! + +Without proper embedding, the iOS app installs but the watch app does NOT install on the paired Apple Watch. + +### Bundle Identifiers + +Watch app bundle IDs MUST be prefixed with the iOS app's bundle ID: + +``` +iOS app: com.company.AppName +Watch app: com.company.AppName.watchkitapp ← MUST start with iOS bundle ID +``` + +Also verify `WKCompanionAppBundleIdentifier` in the watch target's build settings matches the iOS app's bundle ID exactly. + +### Data Sync: WatchConnectivity (NOT App Groups) + +**DO NOT use App Groups for iPhone ↔ Watch data sharing.** + +App Groups: +- ❌ Do NOT work between iPhone and Apple Watch +- ❌ Different container paths on each device +- ❌ Will waste hours debugging why data isn't syncing +- ✅ Only work between an app and its extensions on the SAME device + +**Use WatchConnectivity framework instead:** + +```swift +// iOS side - WatchConnectivityService.swift +import WatchConnectivity + +@MainActor +final class WatchConnectivityService: NSObject, WCSessionDelegate { + static let shared = WatchConnectivityService() + + private override init() { + super.init() + if WCSession.isSupported() { + WCSession.default.delegate = self + WCSession.default.activate() + } + } + + func syncData(_ data: [String: Any]) { + guard WCSession.default.activationState == .activated, + WCSession.default.isPaired, + WCSession.default.isWatchAppInstalled else { return } + + try? WCSession.default.updateApplicationContext(data) + } +} +``` + +### WatchConnectivity Methods + +| Method | Use Case | +|--------|----------| +| `updateApplicationContext` | Latest state that persists (use this for most syncs) | +| `sendMessage` | Immediate delivery when counterpart is reachable | +| `transferUserInfo` | Queued delivery, guaranteed but not immediate | + +### watchOS Framework Limitations + +These iOS frameworks are NOT available on watchOS: + +- ❌ `CoreImage` - Generate QR codes on iOS, send image data to watch +- ❌ `UIKit` (mostly) - Use SwiftUI +- ❌ `AVFoundation` (limited) + +### Simulator Limitations + +WatchConnectivity on simulators is **unreliable**: + +- `isWatchAppInstalled` often returns `false` even when running +- `isReachable` may be `false` even with both apps running +- `updateApplicationContext` may fail with "counterpart not installed" + +**Workarounds for simulator testing:** +1. Add `#if targetEnvironment(simulator)` blocks with sample data +2. Test real sync functionality on physical devices only + +### Debugging Watch Sync Issues + +If `isWatchAppInstalled` returns `false`: + +1. ✅ Check "Embed Watch Content" build phase exists +2. ✅ Check "Code Sign On Copy" is enabled +3. ✅ Verify bundle ID is prefixed correctly +4. ✅ Clean build folder (⇧⌘K) and rebuild +5. ✅ On iPhone, open Watch app → verify app appears under "Installed" + +### NSObject Requirement + +`WCSessionDelegate` is an Objective-C protocol, so conforming classes must inherit from `NSObject`: + +```swift +final class WatchConnectivityService: NSObject, WCSessionDelegate { + // NSObject is required for WCSessionDelegate conformance +} +``` + + ## SwiftData Instructions If SwiftData is configured to use CloudKit: diff --git a/README.md b/README.md index b37cf46..c420ba7 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,16 @@ BusinessCardTests/ # Unit tests **watchOS Target:** - WatchConnectivity (for receiving cards from iPhone) +- Bundle ID must be prefixed with iOS bundle ID (e.g., `com.mbrucedogs.BusinessCard.watchkitapp`) + +### Watch App Embedding (CRITICAL) + +The watch app must be embedded in the iOS app for deployment. In the iOS target's **Build Phases**: + +1. **"Embed Watch Content"** phase must exist +2. **"Code Sign On Copy"** must be CHECKED ✓ + +Without this, the iOS app installs but the watch app does NOT install on the paired Apple Watch. See `Agents.md` for full troubleshooting guide. ### CloudKit Container