From c9ed9489e341a5fa8caad280a2bbf410d00d7682 Mon Sep 17 00:00:00 2001 From: sibylla canton Date: Tue, 3 Nov 2026 16:10:58 +0100 Subject: [PATCH 1/2] fix(ios): configure audio session before playback Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- iosApp/iosApp/VlcPlayerEngine.swift | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/iosApp/iosApp/VlcPlayerEngine.swift b/iosApp/iosApp/VlcPlayerEngine.swift index 32eaddfa..06008f06 100644 --- a/iosApp/iosApp/VlcPlayerEngine.swift +++ b/iosApp/iosApp/VlcPlayerEngine.swift @@ -36,6 +36,7 @@ final class VlcPlayerEngineFactory: NSObject, NativePlayerEngineFactory { /// Every VLC call is made on the main queue, in the order the Kotlin side issued it. final class VlcPlayerEngine: NSObject, NativePlayerEngine, VLCMediaPlayerDelegate { private let player = VLCMediaPlayer() + private let audioSession = AVAudioSession.sharedInstance() private var listener: NativePlayerListener? private var released = false @@ -47,7 +48,6 @@ final class VlcPlayerEngine: NSObject, NativePlayerEngine, VLCMediaPlayerDelegat override init() { super.init() player.delegate = self - try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: []) } // MARK: NativePlayerEngine @@ -70,6 +70,7 @@ final class VlcPlayerEngine: NSObject, NativePlayerEngine, VLCMediaPlayerDelegat } self.lastReportedDurationMs = 0 self.player.media = media + self.applyPlaybackSettings() self.report(.opening) if autoPlay { self.startPlayback() } } @@ -121,11 +122,15 @@ final class VlcPlayerEngine: NSObject, NativePlayerEngine, VLCMediaPlayerDelegat onMain { guard !self.released else { return } self.released = true - self.listener = nil self.player.delegate = nil self.player.stop() self.player.media = nil - try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation) + do { + try self.audioSession.setActive(false, options: .notifyOthersOnDeactivation) + } catch { + self.listener?.onError(message: "Could not deactivate audio session: \(error.localizedDescription)") + } + self.listener = nil } } @@ -172,7 +177,19 @@ final class VlcPlayerEngine: NSObject, NativePlayerEngine, VLCMediaPlayerDelegat // MARK: Internals private func startPlayback() { - try? AVAudioSession.sharedInstance().setActive(true) + do { + try audioSession.setCategory(.playback, mode: .default, options: []) + } catch { + listener?.onError(message: "Could not configure audio session: \(error.localizedDescription)") + return + } + do { + try audioSession.setActive(true) + } catch { + listener?.onError(message: "Could not activate audio session: \(error.localizedDescription)") + return + } + applyPlaybackSettings() player.play() } From 00e0f22567f227cd361961d0b2ea8d51ca41570f Mon Sep 17 00:00:00 2001 From: sibylla canton Date: Tue, 3 Nov 2026 16:40:14 +0100 Subject: [PATCH 2/2] ci(ios): install CocoaPods before build Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ios-cloud-verify.yml | 88 ++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/ios-cloud-verify.yml diff --git a/.github/workflows/ios-cloud-verify.yml b/.github/workflows/ios-cloud-verify.yml new file mode 100644 index 00000000..ac8f655e --- /dev/null +++ b/.github/workflows/ios-cloud-verify.yml @@ -0,0 +1,88 @@ +name: iOS cloud verification + +on: + workflow_dispatch: + inputs: + ref: + description: Branch or commit to verify + required: true + default: ios-background-playback + +jobs: + build-ios: + runs-on: macos-latest + timeout-minutes: 120 + steps: + - name: Checkout Spotube + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + + - name: Checkout ComposeNativeWebview + uses: actions/checkout@v4 + with: + repository: KRTirtho/ComposeNativeWebview + ref: cbd36f88927298a28a2d64c6acf7ec3b2def6f9d + path: compose-webview + + - name: Set up Java + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + + - name: Set up Ruby and CocoaPods + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: false + + - name: Install CocoaPods + run: gem install cocoapods + + - name: Set up Rust + uses: dtolnay/rust-toolchain@stable + + - name: Publish ComposeNativeWebview + working-directory: compose-webview + run: | + chmod +x gradlew + ./gradlew publishToMavenLocal + + - name: Compile Kotlin/Native iOS + run: | + chmod +x gradlew + ./gradlew :composeApp:compileKotlinIosSimulatorArm64 + + - name: Install CocoaPods dependencies + run: | + cd iosApp + pod install --repo-update + + - name: Build unsigned iOS app + run: | + xcodebuild \ + -workspace iosApp/iosApp.xcworkspace \ + -scheme iosApp \ + -configuration Release \ + -sdk iphoneos \ + -destination 'generic/platform=iOS' \ + -derivedDataPath build/ios \ + CODE_SIGNING_ALLOWED=NO \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGN_IDENTITY="" \ + DEVELOPMENT_TEAM="" \ + build + + - name: Package unsigned IPA + run: | + app=$(find build/ios/Build/Products/Release-iphoneos -maxdepth 1 -name '*.app' -type d | head -1) + mkdir Payload + cp -R "$app" Payload/ + zip -qry Spotube-ios-unsigned.ipa Payload + + - name: Upload IPA + uses: actions/upload-artifact@v4 + with: + name: spotube-ios-unsigned + path: Spotube-ios-unsigned.ipa