Platform differences
On iOS, the two capture modes are selected with
createLocalScreenTrack(mode:), defaulting to .inApp:
Basic usage
Create a screen-sharing track with defaults
macOS: choose a display or window
On macOS, your app first enumerates capture sources, then passes the source the user selected to the SDK:- The SDK is responsible for “discovering shareable sources” and “actually starting capture”
- Your app is responsible for “how to present the list of displays / windows to the user”
macOS: exclude your own windows when sharing a full display
When sharing an entire display, the captured content includes your own app’s windows by default (consistent with Zoom / Tencent Meeting). This means any window that’s rendering this shared content—the sharing preview in your UI, or the in-call window showing the remote screen share when you test with two instances on one machine—creates an infinite mirror. Put the IDs of these windows inexcludedWindowIds to cut them out one by one:
- The value is
UInt32(NSWindow.windowNumber). It only matters when sharing an entire display, and is ignored when capturing a single window (WindowSource) - The list is snapshotted once at
startCapture(); windows opened afterward all appear in the capture - If you really want to go back to the old behavior of excluding the entire app from the capture, set
excludesCurrentApplicationtotrue;excludedWindowIdsis then ignored
ScreenCaptureSources.availableWindows(includeCurrentApp:) includes your own windows by default, so sharing one of your own app’s windows is a normal option. But likewise, don’t pick “a window that’s rendering the shared content” as the sharing source.macOS: capture system audio at the same time
If you want to send system audio along with screen sharing, passaudioPreset:
LocalScreenTrack that contains an internal audioTrack, the SDK automatically sends the screen audio along through the audio mixing pipeline.
iOS: in-app screen capture
On iOS you don’t need to passsource:
- This is an in-app capture model, not a desktop-style window selection model
- Once the user switches to another app, no content is captured and the remote side sees a frozen frame
- Even if you pass
audioPreset, iOS doesn’t capture system audio
iOS: full-screen capture (Broadcast Extension)
To capture the entire system screen, iOS offers only one path: ReplayKit’s Broadcast Upload Extension. Capture happens in a separate extension process launched by the system; the SDK carries the frames back to the app process, then encodes and sends them. What your app needs to do is set up the extension target.1. Create the extension target
In Xcode, choose File → New → Target → Broadcast Upload Extension and uncheck “Include UI Extension”. Add theSRTCBroadcastKit dependency to this target (do not add SRTC), and replace the template-generated SampleHandler
entirely with:
2. Configure an App Group
The app and the extension need a shared App Group to exchange capture parameters and host the cross-process channel (the App Group must first be registered in the Apple Developer portal, and the provisioning profiles for both bundle IDs must include this capability):- Enable the App Groups capability on both the app target and the extension target, and check the same group;
- Add it to the extension’s Info.plist:
3. Create the track and start listening
A successful
startCapture() doesn’t mean there’s video yet; it only means the SDK is ready and waiting for the extension to connect.
When the user starts the broadcast isn’t up to the app, so listening must be turned on in advance.4. Let the user start the broadcast
Full-screen capture can only be started by the user from the system UI; the app can’t tap for the user. The SDK wraps the system picker:SRTCBroadcastPickerView. After tapping, the user sees the system broadcast picker panel; only after they select your extension and tap
Start Broadcast does video actually start transmitting.
5. Listen for the real start and end
The user may stop sharing directly from the system pill (the red timer at the top of the screen). This happens outside the app, so you must detect it through events:
To display UI state such as “sharing”, check
isBroadcastActive.
Stop sharing
stopCapture() notifies the extension to end; the extension then exits on its own and the system pill disappears—so there’s a short delay between the call and the pill disappearing.
This is platform behavior, not a sign that the call had no effect.
FAQ
Why must macOS let the user choose a source first?
Because desktop capture is fundamentally “the user authorizing which screen or which window to share”, and the SDK can’t make that choice for the user.Why doesn’t iOS support system audio?
This is a platform capability boundary, not a simple switch at the SDK level. The Swift SDK explicitly ignores the screen audio configuration on iOS rather than providing an API that looks supported but does nothing.No video from full-screen sharing on the Simulator?
Full-screen capture depends on real code signing and an App Group, so it doesn’t work on the Simulator—use a real device. On the Simulator you can only verify that the project compiles.Where do I find the extension’s logs?
The extension is a separate process, so its logs don’t appear in the Xcode console for the main app. Use Console.app to connect to the device and filter by subsystemcom.srtc.broadcast. Two common messages:
- “请先在 App 内开启屏幕共享,再从系统菜单开始广播” (“Start screen sharing in the app first, then start the broadcast from the system menu”)—the user tapped the system pill first, before the app called
startCapture(); the extension can’t connect to the host and ends on its own; - “未配置 App Group” (“App Group not configured”)—
SRTCAppGroupIdentifierin the extension’s Info.plist is missing or misspelled.
Occasional frame skipping during full-screen sharing?
Screen frames go through a fixed-capacity buffer pool on the host side. When encoding can’t keep up, intermediate frames are coalesced and only the latest frame is kept. This is intentional protection (otherwise memory would grow without bound). The SDK periodically logs a summary ofreceived / delivered / coalesced / footprint; if coalesced keeps growing, the current resolution / frame rate is too high for the device,
and you can switch to a lower ScreenPreset.