1920258fd6
ROOT CAUSE of iOS push not working: Capacitor 7's default AppDelegate.swift template does NOT implement application(_:didRegisterForRemoteNotificationsWithDeviceToken:) or ...didFailToRegisterForRemoteNotificationsWithError:. So when @capacitor/push- notifications calls registerForRemoteNotifications(), iOS fetches the APNs token and calls the AppDelegate, but nothing posts .capacitorDidRegisterForRemoteNotifications, so the plugin never delivers the token to JS. register() 'succeeds' yet neither the registration nor registrationError event fires — proven by server push telemetry (register-called logged; no token, no error; device_tokens stayed empty). inject-push.js adds the two forwarding methods to the CI-generated AppDelegate (idempotent, tolerant — never fails the build), wired into ios-patch.sh after the audio patch. Verified against the real Capacitor 7 template: methods land inside the class, braces balance, both listeners present. This is the missing piece alongside the earlier aps-environment entitlement fix and the server APNs config. Needs a fresh Codemagic build to take effect. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
106 lines
6.8 KiB
Bash
106 lines
6.8 KiB
Bash
#!/usr/bin/env bash
|
|
# Patch the freshly-generated Capacitor iOS project (mobile/ios/App) for App Store submission.
|
|
# Runs on the Codemagic macOS instance after `npx cap add ios`. Idempotent — safe to re-run.
|
|
set -euo pipefail
|
|
|
|
PLIST="mobile/ios/App/App/Info.plist"
|
|
PB=/usr/libexec/PlistBuddy
|
|
|
|
set_str() { # set_str <key> <value> — add the key if missing, else overwrite
|
|
"$PB" -c "Add :$1 string $2" "$PLIST" 2>/dev/null || "$PB" -c "Set :$1 $2" "$PLIST"
|
|
}
|
|
|
|
echo "Patching $PLIST"
|
|
|
|
# ── Privacy usage strings (Apple REJECTS the build if a used capability has no purpose string) ──
|
|
set_str NSCameraUsageDescription "Biz Connect uses the camera for video calls and to share photos and your screen."
|
|
set_str NSMicrophoneUsageDescription "Biz Connect uses the microphone for voice and video calls."
|
|
set_str NSPhotoLibraryUsageDescription "Biz Connect needs photo access so you can send images in chat."
|
|
set_str NSPhotoLibraryAddUsageDescription "Biz Connect saves images and recordings you download to your photos."
|
|
|
|
# Human-readable display name on the home screen.
|
|
set_str CFBundleDisplayName "Biz Connect"
|
|
|
|
# CFBundleVersion (build number) MUST be unique AND higher than every previous App Store Connect upload,
|
|
# or publishing fails with "The bundle version must be higher than the previously uploaded version".
|
|
# Capacitor ships "1" by default, so every build collided. Use Codemagic's monotonically-increasing
|
|
# BUILD_NUMBER (this is build index 6+, already > the "1" that's on TestFlight). Fall back to an epoch
|
|
# timestamp if it's somehow unset, which is also strictly increasing.
|
|
BUILD_NO="${BUILD_NUMBER:-$(date +%s)}"
|
|
echo "Setting CFBundleVersion = $BUILD_NO"
|
|
set_str CFBundleVersion "$BUILD_NO"
|
|
|
|
# ── Make the app's Documents folder visible in the Files app ───────────────────────────────────────
|
|
# Downloads are saved to Documents/{Images,Videos,Files}. Without these two keys that folder is private
|
|
# and the user has no way to reach what they saved. With them, Files shows
|
|
# Files → Browse → On My iPhone → Biz Connect → Images / Videos / Files
|
|
# UIFileSharingEnabled exposes the folder; LSSupportsOpeningDocumentsInPlace lets other apps open those
|
|
# files in place rather than silently working on a copy.
|
|
set_bool() { "$PB" -c "Add :$1 bool $2" "$PLIST" 2>/dev/null || "$PB" -c "Set :$1 $2" "$PLIST"; }
|
|
set_bool UIFileSharingEnabled true
|
|
set_bool LSSupportsOpeningDocumentsInPlace true
|
|
|
|
# ── Custom URL scheme so the Share Extension can bounce the user back into the app ──────────────────
|
|
# The extension stages the shared files into the App Group, then opens bizconnect://share; the app reads
|
|
# the staged files and shows "Send to…". Registering the scheme is what makes that openURL succeed.
|
|
if ! "$PB" -c "Print :CFBundleURLTypes" "$PLIST" >/dev/null 2>&1; then
|
|
"$PB" -c "Add :CFBundleURLTypes array" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0 dict" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0:CFBundleURLName string com.bizgaze.connect" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes array" "$PLIST"
|
|
"$PB" -c "Add :CFBundleURLTypes:0:CFBundleURLSchemes:0 string bizconnect" "$PLIST"
|
|
fi
|
|
|
|
# ── Push Notifications entitlement (aps-environment) ────────────────────────────────────────────────
|
|
# The @capacitor/push-notifications plugin does NOT add the Push Notifications capability to the generated
|
|
# Xcode project — in Xcode that's a manual "Signing & Capabilities → + Push Notifications" click, which
|
|
# never happens on a fresh CI checkout. Without the aps-environment entitlement, PushNotifications.register()
|
|
# fails on device ("no valid 'aps-environment' entitlement string found") and NO APNs token is ever
|
|
# obtained, so device_tokens stays empty and the server has nothing to push to. Create the entitlements
|
|
# file with aps-environment HERE; add-share-extension.rb (runs after this) MERGES the App Group into the
|
|
# same file, preserving this key. REQUIRES: the App ID com.bizgaze.connect must have the Push Notifications
|
|
# capability enabled in the Apple Developer portal, so the fetched provisioning profile carries
|
|
# aps-environment — otherwise the archive fails code-signing. "production" is correct for App Store +
|
|
# TestFlight (pair it with APNS_PRODUCTION=1 on the server).
|
|
ENT="mobile/ios/App/App/App.entitlements"
|
|
if [ ! -f "$ENT" ]; then
|
|
cat > "$ENT" <<'PLIST'
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
</dict>
|
|
</plist>
|
|
PLIST
|
|
fi
|
|
"$PB" -c "Add :aps-environment string production" "$ENT" 2>/dev/null || "$PB" -c "Set :aps-environment production" "$ENT"
|
|
echo "Entitlements: aps-environment=production ensured in $ENT"
|
|
|
|
# We use only standard encryption (HTTPS/TLS), which is exempt — declaring this up front stops App Store
|
|
# Connect from asking the "export compliance" question on every single build/TestFlight upload.
|
|
"$PB" -c "Add :ITSAppUsesNonExemptEncryption bool false" "$PLIST" 2>/dev/null \
|
|
|| "$PB" -c "Set :ITSAppUsesNonExemptEncryption false" "$PLIST"
|
|
|
|
# Allow the webview to load our HTTPS origin (we do NOT enable arbitrary cleartext).
|
|
"$PB" -c "Delete :NSAppTransportSecurity" "$PLIST" 2>/dev/null || true
|
|
|
|
# ── Default in-call audio to the LOUDSPEAKER (AVAudioSession) ──────────────────────────────────────
|
|
# Without this, iOS routes WebRTC call audio to the quiet EARPIECE. A Node helper (Node 20 is already set
|
|
# up for this build) injects an AVAudioSession category into the generated AppDelegate so calls default to
|
|
# the speaker (headphones/Bluetooth still win when connected). The helper is tolerant and exits 0 even if
|
|
# the template differs, so it NEVER fails the build. First-pass fix — if WebRTC re-grabs the session
|
|
# mid-call on device, we follow up with a plugin that re-asserts .overrideOutputAudioPort(.speaker).
|
|
AD="mobile/ios/App/App/AppDelegate.swift"
|
|
if [ -f "$AD" ]; then
|
|
echo "Patching AppDelegate audio session"
|
|
node "$(dirname "$0")/inject-audio.js" "$AD" || echo " (AVAudioSession patch skipped — non-fatal)"
|
|
# Capacitor 7's AppDelegate template omits the APNs registration callbacks, so the push-notifications
|
|
# plugin never receives the device token (register() fires no event at all). Inject the forwarding methods.
|
|
echo "Patching AppDelegate APNs registration forwarding"
|
|
node "$(dirname "$0")/inject-push.js" "$AD" || echo " (push forwarding patch skipped — non-fatal)"
|
|
fi
|
|
|
|
echo "Info.plist patched:"
|
|
"$PB" -c "Print :NSCameraUsageDescription" "$PLIST"
|
|
"$PB" -c "Print :NSMicrophoneUsageDescription" "$PLIST"
|