Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
54d4561158
commit
fcc266d795
132
Agents.md
132
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:
|
||||
|
||||
10
README.md
10
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
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user