From 92f1f8f1a3083b5a561842d429a1f6bb67a28d1b Mon Sep 17 00:00:00 2001 From: sravan Date: Mon, 3 Aug 2026 11:48:45 +0530 Subject: [PATCH] =?UTF-8?q?Capacitor=208=20=E2=80=94=20Step=20B:=20migrate?= =?UTF-8?q?=20iOS=20from=20CocoaPods=20to=20Swift=20Package=20Manager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move off CocoaPods (sunsetting; trunk goes read-only Dec 2026) to SPM, the Cap 8 default. This also ends the LiveKit git-pin hack — LiveKit becomes a real SPM dep. - Each local plugin gets a Package.swift (swift-tools 5.9, iOS 15, capacitor-swift-pm from 8.0.0, source path ios/Sources/). native-call additionally declares LiveKit: .package(client-sdk-swift, exact 2.15.3) + product "LiveKit". - native-call Swift: import LiveKitClient -> import LiveKit (SPM product name). - codemagic.yaml: `cap add ios --packagemanager SPM`; removed the "Install CocoaPods" (pod install) step; XCODE_WORKSPACE -> XCODE_PROJECT (App.xcodeproj); build-ipa --workspace -> --project. Xcode resolves the Swift packages during archive. - ios-patch.sh: removed the Podfile LiveKit git-pin injection + Podfile.lock deletion (no Podfile under SPM). Info.plist/entitlements/AppDelegate/notif.wav patches stay. - add-share-extension.rb is SPM-safe (operates on App.xcodeproj, no workspace/Pods refs). Authored blind (no local Xcode) — expect build iterations on the first SPM build (cap-add SPM layout, CapApp-SPM plugin wiring, LiveKit SPM resolution, signing). Fully revertible: git revert -> back to Cap 8 + CocoaPods (working). Co-Authored-By: Claude Opus 4.8 --- codemagic.yaml | 26 +++++------ mobile/plugins/audio-route/Package.swift | 22 +++++++++ mobile/plugins/media-library/Package.swift | 22 +++++++++ mobile/plugins/native-call/Package.swift | 27 +++++++++++ .../NativeCallPlugin/NativeCallPlugin.swift | 2 +- mobile/plugins/share-inbox/Package.swift | 22 +++++++++ mobile/scripts/ios-patch.sh | 45 +++---------------- 7 files changed, 110 insertions(+), 56 deletions(-) create mode 100644 mobile/plugins/audio-route/Package.swift create mode 100644 mobile/plugins/media-library/Package.swift create mode 100644 mobile/plugins/native-call/Package.swift create mode 100644 mobile/plugins/share-inbox/Package.swift diff --git a/codemagic.yaml b/codemagic.yaml index c059582..7d3dcc4 100644 --- a/codemagic.yaml +++ b/codemagic.yaml @@ -27,7 +27,7 @@ workflows: - ios_signing # ← Codemagic variable group holding CERTIFICATE_PRIVATE_KEY (secure). See below. vars: BUNDLE_ID: "com.bizgaze.connect" - XCODE_WORKSPACE: "mobile/ios/App/App.xcworkspace" + XCODE_PROJECT: "mobile/ios/App/App.xcodeproj" XCODE_SCHEME: "App" node: 22 xcode: latest @@ -44,13 +44,14 @@ workflows: script: | cd mobile # `cap add ios` scaffolds ios/App; safe to re-run — it no-ops if it already exists. - # Capacitor 8 DEFAULTS to Swift Package Manager, but our whole iOS pipeline (LiveKit Podfile pins, - # share-extension injection, AppDelegate patches) is CocoaPods-based — so force CocoaPods. - if [ ! -d "ios" ]; then npx cap add ios --packagemanager Cocoapods; fi + # Capacitor 8 Swift Package Manager: generate an SPM project (no Podfile). LiveKit is pulled via the + # native-call plugin's Package.swift; `cap sync` wires all local plugins into the CapApp-SPM package. + if [ ! -d "ios" ]; then npx cap add ios --packagemanager SPM; fi npx cap sync ios - # Fail LOUDLY if no Podfile was generated (flag ignored / SPM project) rather than fail later with a - # confusing error — the CocoaPods pipeline downstream needs ios/App/Podfile to exist. - test -f ios/App/Podfile || { echo "ERROR: no ios/App/Podfile — 'cap add' did not use CocoaPods"; exit 1; } + # Sanity: the Xcode project must exist. Print the iOS dir so the log shows the SPM layout (CapApp-SPM + # present, NO Podfile) — if a Podfile appears, the SPM flag didn't take and we'd need to fix it. + test -d ios/App/App.xcodeproj || { echo "ERROR: iOS Xcode project not generated"; ls -la ios/App || true; exit 1; } + echo "iOS project layout:"; ls -la ios/App # App icon + splash from resources/icon.png & resources/splash*.png (1024x1024 icon, 2732² splash). npx capacitor-assets generate --ios || echo "asset generation skipped" @@ -95,13 +96,8 @@ workflows: --create keychain add-certificates - - name: Install CocoaPods - script: | - cd mobile/ios/App - # --repo-update refreshes the cached spec repo so it knows LiveKit 2.15.3's transitive deps - # (LiveKitUniFFI 0.0.6 + LiveKitWebRTC 144.7559.11) — they ARE on the trunk but the build box's - # cached repo was stale ("Unable to find a specification for LiveKitUniFFI (= 0.0.6)"). - pod install --repo-update + # (No "Install CocoaPods" step under SPM — there is no Podfile. Xcode resolves the Swift packages + # (Capacitor, plugins, LiveKit + its WebRTC/UniFFI/SwiftProtobuf) during the archive below.) - name: Build the signed IPA script: | @@ -112,7 +108,7 @@ workflows: # `xcode-project build-ipa` prints a PRETTIFIED summary and swallows the raw xcodebuild "error:" # lines — a failed archive shows only "Failed to archive" with no reason. On failure, surface the # actual errors from the raw log so we don't have to dig through the artifact. - if ! xcode-project build-ipa --workspace "$XCODE_WORKSPACE" --scheme "$XCODE_SCHEME"; then + if ! xcode-project build-ipa --project "$XCODE_PROJECT" --scheme "$XCODE_SCHEME"; then echo "======================= xcodebuild errors =======================" grep -h -E "error:|errSec|Provisioning profile|entitlement|Code ?Sign|does not (support|contain)|requires a provisioning|No profile|No signing|doesn't (include|match)|Command .* failed" /tmp/xcodebuild_logs/*.log 2>/dev/null | grep -vi "warning:" | tail -60 || echo "(no matching lines — open the xcodebuild_logs artifact)" echo "=================================================================" diff --git a/mobile/plugins/audio-route/Package.swift b/mobile/plugins/audio-route/Package.swift new file mode 100644 index 0000000..7044d4a --- /dev/null +++ b/mobile/plugins/audio-route/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version: 5.9 +import PackageDescription + +let package = Package( + name: "AudioRoute", + platforms: [.iOS(.v15)], + products: [ + .library(name: "AudioRoute", targets: ["AudioRoutePlugin"]) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0") + ], + targets: [ + .target( + name: "AudioRoutePlugin", + dependencies: [ + .product(name: "Capacitor", package: "capacitor-swift-pm"), + .product(name: "Cordova", package: "capacitor-swift-pm") + ], + path: "ios/Sources/AudioRoutePlugin") + ] +) diff --git a/mobile/plugins/media-library/Package.swift b/mobile/plugins/media-library/Package.swift new file mode 100644 index 0000000..85f944a --- /dev/null +++ b/mobile/plugins/media-library/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version: 5.9 +import PackageDescription + +let package = Package( + name: "MediaLibrary", + platforms: [.iOS(.v15)], + products: [ + .library(name: "MediaLibrary", targets: ["MediaLibraryPlugin"]) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0") + ], + targets: [ + .target( + name: "MediaLibraryPlugin", + dependencies: [ + .product(name: "Capacitor", package: "capacitor-swift-pm"), + .product(name: "Cordova", package: "capacitor-swift-pm") + ], + path: "ios/Sources/MediaLibraryPlugin") + ] +) diff --git a/mobile/plugins/native-call/Package.swift b/mobile/plugins/native-call/Package.swift new file mode 100644 index 0000000..e2abcad --- /dev/null +++ b/mobile/plugins/native-call/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version: 5.9 +import PackageDescription + +// SPM manifest for the native-call Capacitor plugin (Capacitor 8 uses SPM). LiveKit is declared here as a +// REAL SPM dependency — LiveKit 2.1+ is SPM-native, so this replaces the CocoaPods git-tag pin hack entirely. +// SPM resolves LiveKit + its LiveKitWebRTC / LiveKitUniFFI / SwiftProtobuf sub-packages directly. +let package = Package( + name: "NativeCall", + platforms: [.iOS(.v15)], + products: [ + .library(name: "NativeCall", targets: ["NativeCallPlugin"]) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"), + .package(url: "https://github.com/livekit/client-sdk-swift.git", exact: "2.15.3") + ], + targets: [ + .target( + name: "NativeCallPlugin", + dependencies: [ + .product(name: "Capacitor", package: "capacitor-swift-pm"), + .product(name: "Cordova", package: "capacitor-swift-pm"), + .product(name: "LiveKit", package: "client-sdk-swift") + ], + path: "ios/Sources/NativeCallPlugin") + ] +) diff --git a/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift b/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift index 49c4a9d..be92119 100644 --- a/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift +++ b/mobile/plugins/native-call/ios/Sources/NativeCallPlugin/NativeCallPlugin.swift @@ -3,7 +3,7 @@ import Capacitor import PushKit import CallKit import AVFoundation -import LiveKitClient // the CocoaPod is 'LiveKitClient' (no module_name), so the Swift module is LiveKitClient — NOT LiveKit +import LiveKit // SPM product name is 'LiveKit' (Package.swift). (The old CocoaPods module was 'LiveKitClient'.) // Native calling for Biz Connect (iOS). Registered by `cap sync` as window.Capacitor.Plugins.NativeCall. // diff --git a/mobile/plugins/share-inbox/Package.swift b/mobile/plugins/share-inbox/Package.swift new file mode 100644 index 0000000..f066cbd --- /dev/null +++ b/mobile/plugins/share-inbox/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version: 5.9 +import PackageDescription + +let package = Package( + name: "ShareInbox", + platforms: [.iOS(.v15)], + products: [ + .library(name: "ShareInbox", targets: ["ShareInboxPlugin"]) + ], + dependencies: [ + .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0") + ], + targets: [ + .target( + name: "ShareInboxPlugin", + dependencies: [ + .product(name: "Capacitor", package: "capacitor-swift-pm"), + .product(name: "Cordova", package: "capacitor-swift-pm") + ], + path: "ios/Sources/ShareInboxPlugin") + ] +) diff --git a/mobile/scripts/ios-patch.sh b/mobile/scripts/ios-patch.sh index 0cff121..54f5e53 100644 --- a/mobile/scripts/ios-patch.sh +++ b/mobile/scripts/ios-patch.sh @@ -123,46 +123,11 @@ if [ -f "$AD" ]; then node "$(dirname "$0")/inject-push.js" "$AD" || echo " (push forwarding patch skipped — non-fatal)" fi -# ── Pin LiveKit to 2.15.3 via its Git tag (stay on CocoaPods; no SPM migration) ───────────────────── -# The CallKit audio-session coordination API (AudioManager.audioSession / setEngineAvailability) exists only -# in LiveKit 2.1+, but the LiveKitClient CocoaPod PUBLISHED to trunk caps at 2.0.18 (2.1+ is SPM-only). The -# repo still ships a VALID podspec at tag 2.15.3, and its deps (LiveKitWebRTC 144.7559.11 / LiveKitUniFFI -# 0.0.6 / SwiftProtobuf) ARE on trunk — so we point the Podfile straight at the git tag. The NativeCall -# podspec's `LiveKitClient ~> 2.0` is satisfied by 2.15.3 (2.15.3 ∈ [2.0, 3.0)). Idempotent (grep guard). -PODFILE="mobile/ios/App/Podfile" -if [ -f "$PODFILE" ]; then - if grep -q "client-sdk-swift.git" "$PODFILE"; then - echo "Podfile: LiveKitClient git pin already present" - else - ruby -e ' - p = "mobile/ios/App/Podfile" - s = File.read(p) - # LiveKit 2.15.3 pulls two binary deps that are SPM-only (NOT on the CocoaPods CDN), so we pin each to - # its own repo podspec (neither has further deps): - # * LiveKitUniFFI 0.0.6 — podspec has a :git source + prepare_command that downloads its xcframework, so :git works. - # * LiveKitWebRTC 144.7559.11 — podspec has an :http source (release-zip), NOT in the git tree, so we - # point at the podspec URL with :podspec (using :git would clone a repo with no xcframework and fail). - # SwiftProtobuf (the only other dep) resolves from the CDN normally. - pin = " pod \x27LiveKitClient\x27, :git => \x27https://github.com/livekit/client-sdk-swift.git\x27, :tag => \x272.15.3\x27\n" + - " pod \x27LiveKitUniFFI\x27, :git => \x27https://github.com/livekit/livekit-uniffi-xcframework.git\x27, :tag => \x270.0.6\x27\n" + - " pod \x27LiveKitWebRTC\x27, :podspec => \x27https://raw.githubusercontent.com/livekit/webrtc-xcframework/144.7559.11/LiveKitWebRTC.podspec\x27\n" - if s =~ /target ["\x27]App["\x27] do\n/ - s = s.sub(/target ["\x27]App["\x27] do\n/) { |m| m + pin } - File.write(p, s) - puts "Podfile: pinned LiveKitClient 2.15.3 + LiveKitUniFFI 0.0.6 + LiveKitWebRTC 144.7559.11" - else - STDERR.puts "WARN: could not find \"target App do\" in Podfile — LiveKit pin NOT applied" - exit 1 - end - ' - fi - grep -n "LiveKitClient" "$PODFILE" || true - # `npx cap sync` already ran `pod install` with the PRE-pin Podfile, leaving a Podfile.lock that pins - # LiveKitClient = 2.0.18 — which conflicts with the git-tag source we just injected ("could not find - # compatible versions … In snapshot (Podfile.lock): LiveKitClient (= 2.0.18)"). Drop the lock so the - # later "Install CocoaPods" step re-resolves cleanly against tag 2.15.3. - rm -f "mobile/ios/App/Podfile.lock" && echo "Removed stale Podfile.lock (cap-sync pinned 2.0.18)" -fi +# ── LiveKit under SPM (Capacitor 8) ───────────────────────────────────────────────────────────────── +# LiveKit is now a proper Swift Package Manager dependency declared in the native-call plugin's Package.swift +# (github.com/livekit/client-sdk-swift, exact 2.15.3) — SPM resolves it + its WebRTC/UniFFI/SwiftProtobuf +# sub-packages at build time. So there is NO Podfile to patch here anymore (the old CocoaPods git-tag pin +# hack is gone). echo "Info.plist patched:" "$PB" -c "Print :NSCameraUsageDescription" "$PLIST"