feat: capture the screen in-app with ScreenCaptureKit (drop ReplayKit extension) - #14
Merged
Merged
Conversation
… extension) Replace the ReplayKit broadcast upload extension with an in-app iOS 27 ScreenCaptureKit pipeline. Capture, compositing, and publishing now all run in the host process, which removes the fragile cross-process dance the extension required. Capture - ScreenCaptureController drives SCContentSharingPicker + SCStream, paces the native-refresh feed down to the target frame rate by PTS deadline, composites the optional facecam, and feeds the existing publishers. Simulator/preview builds get a device-only stub. - The RTMP/SRT/WHIP publishers, network path monitor, and facecam capture/compositor are process-agnostic, so they now compile directly into the app target instead of the extension. Removals - Delete the broadcast extension target and SampleHandler, the AudioSessionConfigurator, and the host-app keepalive/monitor/picker machinery that existed only to keep the suspended extension alive. App configuration - Bump the deployment target to iOS 27, add the screen-capture background mode and NSScreenCaptureUsageDescription, and allow landscape orientations for landscape capture. Tests - Add a StreamCoreTests unit-test target (Swift Testing) covering the Keychain store, protocol handling, settings codable/backup/publish logic, and the cross-process contract keys; enable testability on the StreamCore Debug config. Misc - Add a Haptics helper for consistent control feedback and rewrite the README around the in-app capture flow. - Advance the vendored HaishinKit pointer to include VideoToolbox invalid-session recovery, so a compression session invalidated during a background transition is rebuilt instead of failing every frame. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AVAudioSession.activate(options:)/deactivate(options:) are watchOS-only and
fail to compile on iOS ('activate(options:)' is unavailable in iOS). Replace
all four call sites (AudioInputProvider, SettingsView, and ScreenCaptureController)
with the synchronous iOS API setActive(_:options:). CI only caught the first
because the ScreenCaptureController calls are device-only and compiled out on
the simulator.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitHub runners ship only the iOS 26 SDK (newest hosted Xcode is 26.6), but the app now requires the iOS 27 SDK (ScreenCaptureKit + the AVAudioInputNode throwing tap), so the full app can't build in CI until runners gain Xcode 27. Add a StreamCoreTests scheme that builds only StreamCore + its Swift Testing suite (pure Foundation/Security logic, no HaishinKit/ScreenCaptureKit) and pin those two targets to an iOS 18 floor so they build and run on whatever iOS simulator the runner ships. CI now runs the 45-test suite; the full app is verified on a physical iOS 27 device (ScreenCaptureKit is device-only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the ReplayKit broadcast upload extension with an in-app iOS 27 ScreenCaptureKit pipeline. Capture, compositing, and publishing now all run in the host process, removing the fragile cross-process keepalive machinery the extension required.
What changed
Capture
ScreenCaptureControllerdrivesSCContentSharingPicker+SCStream, paces the native-refresh feed down to the target frame rate by PTS deadline, composites the optional facecam, and feeds the existing publishers. Simulator/preview builds fall back to a device-only stub.NetworkPathMonitor, and facecam capture/compositor are process-agnostic, so they now compile directly into the app target instead of the extension.Removals
StreamBroadcastextension target andSampleHandler, theAudioSessionConfigurator, and the host-appBroadcastKeepAlive/BroadcastMonitor/BroadcastPickerViewmachinery that existed only to keep the suspended extension alive.App configuration
screen-capturebackground mode +NSScreenCaptureUsageDescription, and enables landscape orientations.Tests
StreamCoreTestsunit-test target (Swift Testing) covering the Keychain store, protocol handling, settings codable/backup/publish logic, and cross-process contract keys; enables testability on the StreamCore Debug config and wires the target into the scheme'stestaction.Misc
Hapticshelper for consistent control feedback and rewrites the README around the in-app flow.kVTInvalidSessionErr.Notes
fix/rtmp-transient-send-keepopen(407adcf4) so CI's recursive checkout resolves it.#elsestub keeps the simulator build (and CI) green.🤖 Generated with Claude Code