---
description: Verify iOS/mobile code compiles, all files are in Xcode targets, and passes quality checks
---

## Mobile Code Quality

Pre-flight quality gate for iOS mobile code. Run this **before** any TestFlight release
or after adding/renaming Swift files.

---

### Step 1 — Verify git working tree is clean

// turbo
```bash
git diff --quiet && git diff --cached --quiet && echo "Working tree clean" || echo "WARNING: uncommitted changes"
```

### Step 2 — Install CocoaPods (if needed)

// turbo
```bash
cd mobile_app/ios && pod install --repo-update && cd ../..
```

### Step 3 — Build ALL iOS targets

Build both the main app and the keyboard extension to catch missing files, type errors, etc.

```bash
xcodebuild build \
  -workspace mobile_app/ios/LysnrAI.xcworkspace \
  -scheme LysnrAI \
  -configuration Debug \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
  CODE_SIGN_IDENTITY=- \
  CODE_SIGNING_ALLOWED=NO \
  2>&1 | tail -30
```

> The LysnrKeyboard extension is embedded in the LysnrAI scheme, so building LysnrAI
> also compiles LysnrKeyboard. If the keyboard target has missing source files (like
> the LysnrTelemetry.swift incident), this step will catch it.

### Step 4 — Verify pbxproj ↔ filesystem consistency

Check that every `.swift` file on disk in the keyboard extension directory is listed in the Xcode project.

// turbo
```bash
echo "=== Checking LysnrKeyboard target source consistency ==="
for f in mobile_app/ios/LysnrKeyboard/*.swift; do
  fname=$(basename "$f")
  if ! grep -q "$fname" mobile_app/ios/LysnrAI.xcodeproj/project.pbxproj; then
    echo "ERROR: $fname exists on disk but is NOT in project.pbxproj"
    exit 1
  fi
done
echo "All keyboard .swift files are in the Xcode project"

echo "=== Checking LysnrAI main app source consistency ==="
missing=0
for f in $(find mobile_app/ios/LysnrAI -name '*.swift' -not -path '*/Pods/*'); do
  fname=$(basename "$f")
  if ! grep -q "$fname" mobile_app/ios/LysnrAI.xcodeproj/project.pbxproj; then
    echo "WARNING: $fname exists on disk but may not be in project.pbxproj"
    missing=$((missing + 1))
  fi
done
if [ "$missing" -eq 0 ]; then
  echo "All main app .swift files are in the Xcode project"
else
  echo "$missing file(s) may be missing from project (review warnings above)"
fi
```

### Step 5 — Check for common Swift issues

// turbo
```bash
echo "=== Checking for hardcoded secrets ==="
grep -rn --include='*.swift' -E '(AZURE_SPEECH_KEY|api_key|secret|password)\s*=' mobile_app/ios/ \
  | grep -v 'forKey:' | grep -v '//' | grep -v 'UserDefaults' | grep -v 'loadEnvValue' \
  || echo "No hardcoded secrets found"

echo ""
echo "=== Checking for print() in production code ==="
grep -rn --include='*.swift' '^\s*print(' mobile_app/ios/LysnrKeyboard/ mobile_app/ios/LysnrAI/ \
  | grep -v '// DEBUG' | grep -v 'test' \
  || echo "No print() statements found (good — use os.Logger)"

echo ""
echo "=== Checking for force-unwraps ==="
grep -rn --include='*.swift' '[^?]![^=]' mobile_app/ios/LysnrKeyboard/*.swift \
  | grep -v '//' | grep -v 'IBOutlet' | grep -v 'IBAction' \
  | head -10 \
  || echo "No suspicious force-unwraps found"
```

### Step 6 — Verify Info.plist keys

// turbo
```bash
echo "=== Checking required Info.plist keys ==="
for key in NSMicrophoneUsageDescription NSSpeechRecognitionUsageDescription NSExtensionPointIdentifier RequestsOpenAccess; do
  if grep -q "$key" mobile_app/ios/LysnrKeyboard/Info.plist; then
    echo "OK: $key present in LysnrKeyboard/Info.plist"
  else
    echo "ERROR: $key MISSING from LysnrKeyboard/Info.plist"
  fi
done

echo ""
if grep -q "com.apple.security.application-groups" mobile_app/ios/LysnrKeyboard/LysnrKeyboard.entitlements; then
  echo "OK: App Group entitlement present in LysnrKeyboard"
else
  echo "ERROR: App Group entitlement MISSING from LysnrKeyboard"
fi
```

---

## Reference

### What This Catches

| Check | Prevents |
|-------|----------|
| Build all targets | Missing source files in Xcode project (e.g. LysnrTelemetry.swift) |
| pbxproj consistency | New .swift files added to disk but not to Xcode target |
| Secret scan | Hardcoded API keys in Swift source |
| print() check | Debug logging in production keyboard extension |
| Force-unwrap check | Runtime crashes from unsafe unwraps |
| Info.plist check | Missing privacy descriptions → App Store rejection |
| Entitlements check | Missing App Group → keyboard can't share data with main app |

### Key Files

| File | Purpose |
|------|---------|
| `mobile_app/ios/LysnrAI.xcodeproj/project.pbxproj` | Xcode project (targets, sources, build phases) |
| `mobile_app/ios/LysnrKeyboard/` | Keyboard extension sources |
| `mobile_app/ios/LysnrKeyboard/Info.plist` | Extension config + privacy descriptions |
| `mobile_app/ios/LysnrKeyboard/LysnrKeyboard.entitlements` | App Group entitlement |
| `mobile_app/ios/Podfile` | CocoaPods dependencies |
