# Core features Enhance the collaborative experience of a session by allowing users in a session to share their screen content on an iPhone or iPad. This guide provides an overview of the two screen sharing implementation options that are supported by the Video SDK for iOS: - Broadcast the device screen: This approach uses broadcasting with the `ZoomVideoSDKScreenShare.xcframework`, [ReplayKit](https://developer.apple.com/documentation/replaykit), and other Apple video frameworks to share a user's full device screen in a session. - Share a `UIView`: Use this approach to share a single `UIView` in a session. These two approaches require the `CptShare.xcframework`. ## Types of screen sharing This table shows the differences between the screen sharing options and things to consider when implementing them. | Broadcast the device screen | Share a single `UIView` | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | | Shares the entire screen in the session. | Only shares a single `UIView` in the session | | Broadcasting happens at the operating system level, not application level. | `UIView` sharing happens at the application level, not at the operating system level. | | Uses an application extension. The application extension, not the application itself, controls the sharing. Use App Group IDs to leverage communication between the application and application extension. | Doesn't require an application extension. | | Highly optimized - it is a native operating system level implementation provided by Apple. | Has optimization limitations. | |   | Not encouraged for complex views. | ## Broadcast the device screen This tutorial demonstrates the steps required to enable and start broadcasting a user's screen in a session using `ReplayKit` and the Video SDK. ### Prerequisites - You have a good understanding of [Broadcast Extensions](https://developer.apple.com/app-extensions/), [AppGroupIDs](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups?language=objc), and [ReplayKit](https://developer.apple.com/documentation/replaykit). Zoom uses these technologies and various Apple video frameworks to implement screen sharing. If you're unfamiliar with these topics, we encourage you to gain an understanding before you begin. - You have [integrated](/docs/video-sdk/ios/integrate/) the `ZoomVideoSDK.xcframework` into your project and implemented a basic [video session](/docs/video-sdk/ios/video/) feature. ### 1. Create a new target Use the **Broadcast Upload Extension** template to create a new target in your app. Click **File->New->Target**, select **Broadcast Upload Extension** in the iOS tab, and click **Next**. Set the target name as **ScreenShare**, select your desired language, and click **Finish**. If you are prompted to activate a scheme, click **Activate**. After the target creation, **Replaykit** framework will be added automatically under **Frameworks and Libraries** option of the **ScreenShare** target. Ensure that the **Do not Embed** option is applied to this framework. ### 2. Configure target settings The **Video SDK** does not support **Bitcode**. Therefore, you must disable bitcode for your targets. Under **Targets**, navigate to **ScreenShare** -> **Build Settings** -> **Build Options** -> **Setting** and set the value of **Enable Bitcode** setting to **No**. Repeat this step for your main target if you haven't already done so. > Ensure that the deployment target version is lower than or equal to your device's OS version for both the main target and the **ScreenShare** target. The **ZoomVideoSDKScreenShare** framework utilizes C++ libraries. To make your project compatible with these libraries, make the following changes within your **ScreenShare** target: - If you are using Objective-C, navigate to **ScreenShare** -> **SampleHandler.m** and rename it to **SampleHandler.mm**. - If you are using Swift, navigate to the **ScreenShare** target -> **Build Settings** -> **Other Linker Flags**, and add the value "**-lc++**". Add **Background** mode in the project target to enable screen share when minimizing the app. To do so, go to **Project_Target** -> **Signing & Capabilities** -> **Background Modes** -> and select **Audio**, **AirPlay** and **Picture in Picture**. ### 3. Import frameworks Under Frameworks, Libraries, and Embedded Content, copy the `CptShare.xcframework` into your Xcode project folder and "Embed & Sign" it. Import the **ZoomVideoSDKScreenShare** framework into the **ScreenShare** app extension target. You can do so by either dragging the framework into Xcode or by navigating to the **Frameworks** section of the ScreenShare target. If you are asked to specify the target, select the **ScreenShare** target (not the main target). The **ZoomVideoSDKScreenShare** framework utilizes the following Apple video frameworks to improve the screen sharing experience. - CoreGraphics.framework - CoreVideo.framework - CoreMedia.framework - VideoToolbox.framework Navigate to the **Frameworks and Libraries** option in the **General** tab of the **ScreenShare** target and click the "**+**" icon to search and include these frameworks. > In certain versions of Xcode, you may have to navigate to **Build Phases** -> **Link Binary with Libraries** to import these frameworks. ### 4. Implement a bridging header The **ZoomVideoSDKScreenShare** framework works by utilizing the callbacks within the SampleHandler. If you are using **Swift** in your project , you must implement a bridging header to expose the **ZoomVideoSDKScreenShare** framework to **SampleHandler.swift**. If you are using **Objective-C** instead of Swift in your project, skip to the next step. Create a temporary **Objective-C** file within your target by navigating to **SampleHandler.swift** in the project explorer, and clicking **File** -> **New File** -> **Objective-C File**. Give this file a name and click **Next**. Add this file to the **ScreenShare** target only and click **Create**. Once **Xcode** prompts you to create an Objective-C bridging header, click **Create Bridging Header**. If you didn't see a prompt, you must [create the bridging header manually](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift) within the **ScreenShare** target. After completing these steps, in the **ScreenShare** target, you should see the bridging header file. The file name contains the product module name followed by `-Bridging-Header.h`. ![](/img/1605632237588.png) Import the **ZoomVideoSDKScreenShareService** framework by adding the following line of code in the bridging header file (`ScreenShare-Bridging-Header.h`): ```objectivec #import ``` > This framework will now be exposed to all the Swift files in your ScreenShare target. Thus, you do not need to include import statements for this framework in your Swift files. You may now delete the temporary **Objective-C** file(not the bridging header file) that you created earlier. ### 5. Set up SampleHandler The `SampleHandler` class is where the code to handle different broadcasting events resides. To handle the events, you must: 1. Conform `SampleHandler` to `ZoomVideoSDKScreenShareServiceDelegate` to establish communication between `ReplayKit` and the **Video SDK**. 2. Pass the `SampleHandler` callbacks into the VideoSDK. To do this, create a `ZoomVideoSDKScreenShare` property, assign the `SampleHandler` as its delegate, and call the delegate functions from the relative `SampleHandler` callbacks. **`SampleHandler.swift`** ```swift import Foundation import ReplayKit class SampleHandler: RPBroadcastSampleHandler, ZoomVideoSDKScreenShareServiceDelegate { var screenShareService: ZoomVideoSDKScreenShareService? override init() { super.init() // Create an instance of ZoomVideoSDKScreenShareService to handle broadcast actions. let params = ZoomVideoSDKScreenShareServiceInitParams() // Provide your app group ID from your Apple Developer account. params.appGroupId = "your app group ID here" // Set this to true to enable sharing device audio during screenshare params.isWithDeviceAudio = true let service = ZoomVideoSDKScreenShareService(params: params) self.screenShareService = service screenShareService?.delegate = self } override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) { guard let setupInfo = setupInfo else { return } // Pass setup info to SDK. screenShareService?.broadcastStarted(withSetupInfo: setupInfo) } override func broadcastPaused() { // Notify SDK the broadcast was paused. screenShareService?.broadcastPaused() } override func broadcastResumed() { // Notify SDK the broadcast was resumed. screenShareService?.broadcastResumed() } override func broadcastFinished() { // Notify SDK the broadcast has finished. screenShareService?.broadcastFinished() } override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) { // Pass sample bugger into SDK. screenShareService?.processSampleBuffer(sampleBuffer, with: sampleBufferType) } func zoomVideoSDKScreenShareServiceFinishBroadcastWithError(_ error: Error?) { guard let error = error else { return } // Terminate broadcast when notified of error from SDK. finishBroadcastWithError(error) } } ``` **`SampleHandler.m`** ```objectivec #import "SampleHandler.h" #import @interface SampleHandler () @property (strong, nonatomic) ZoomVideoSDKScreenShareService * screenShareService; @end @implementation SampleHandler - (instancetype)init { self = [super init]; if (self) { // Create an instance of ZoomVideoSDKScreenShareService to handle broadcast actions. ZoomVideoSDKScreenShareServiceInitParams *params = [[ZoomVideoSDKScreenShareServiceInitParams alloc] init]; // Provide your app group id from your Apple Developer account. params.appGroupId = @"your app group ID here"; // Set this to true to enable sharing device audio during screenshare params.isWithDeviceAudio = YES; ZoomVideoSDKScreenShareService * service = [[ZoomVideoSDKScreenShareService alloc]initWithParams:params]; self.screenShareService = service; self.screenShareService.delegate = self; } return self; } - (void)dealloc { self.screenShareService = nil; } - (void)broadcastStartedWithSetupInfo:(NSDictionary *)setupInfo { // User has requested to start the broadcast. Setup info from the UI extension can be supplied but optional. [self.screenShareService broadcastStartedWithSetupInfo:setupInfo]; } - (void)broadcastPaused { [self.screenShareService broadcastPaused]; // User has requested to pause the broadcast. Samples will stop being delivered. } - (void)broadcastResumed { [self.screenShareService broadcastResumed]; // User has requested to resume the broadcast. Samples delivery will resume. } - (void)broadcastFinished { // User has requested to finish the broadcast. [self.screenShareService broadcastFinished]; } - (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { [self.screenShareService processSampleBuffer:sampleBuffer withType:sampleBufferType]; } - (void)ZoomVideoSDKScreenShareServiceFinishBroadcastWithError:(NSError *)error { [self finishBroadcastWithError:error]; } @end ``` > If using the virtual speaker, screen share will only share video data and will not share the audio data. ### 6. Set up App Groups Use **App Groups** to establish communication between your **ScreenShare** target, and your application's main target. Enable **App Groups** by navigating to your application's main target -> **Signing and Capabilities** -> **+ Capability** and selecting **App Groups**. ![](/img/1605633308842.png) Click the "**+**" icon at the bottom of the **App Groups** section to create a new **App Group** in your main target and set its name. This name will serve as the **App Group ID**. Similar to Bundle IDs, these are in reverse domain order starting with "group.". We recommend appending your **Bundle ID** to "group." to form the **App Group ID** as it is easy to keep track of. The following image shows an example of what an App Group ID could look like. You must create your own unique App Group ID. ![](/img/1605633669302.png) Next, enable this **App Group** by selecting the checkbox next to the **App Group ID**. ![](/img/1605633734899.png) Repeat the above steps to enable the same **App Group** in your **Screenshare** extension target. Note that the same **App Group ID** must be used in both targets. After completing these steps, navigate to the code where you initialized the Video SDK in your application and pass the **App Group ID** to your **SDKInitContext** object. ```swift initParams.appGroupId = "Your AppGroupId." ``` ```objectivec initParams.appGroupId = @"Your AppGroupId."; ``` Next, pass the App Group ID into the `ZoomVideoSDKScreenShare` within your `SampleHandler`. Add the following code inside the `init` method where the other `screenShareService` properties are set. Add the code to `SampleHandler.swift`. ```swift //Pass your App Group ID to the SDK. screenShareService?.appGroup = "Your AppGroupId." ``` Add the code to `SampleHandler.m`. ```objectivec //Pass your App Group ID to the SDK. self.screenShareService.appGroup = @"Your AppGroupId."; ``` ### Run the app Run your app extension to start broadcasting. To do this, first select the **ScreenShare** scheme at the top left corner of Xcode and click **Run**. ![](/img/1605635117990.png) If you're prompted to choose an app to run, select your main target application and click **Run**. > **Note** > > To avoid this prompt and allow Xcode to select your application for you, click your **ScreenShare** scheme -> **Edit Scheme** and select your application under "Executable". By default, you can either debug your **ScreenShare** extension or your main application. If you would like to debug both, select "Debug executable". While your application is running, navigate to the **Control Center** of your iOS device, tap the **Screen Record** button, choose the **ScreenShare** application extension and tap the **Start Broadcast** option to start broadcasting your screen. ![](/img/1605636643466.png) ### Manage the display of the shared content Before the local user starts broadcasting a session, get the `ZoomVideoSDKShareHelper` to receive its share status under the `onUserShareStatusChanged` callback function. Before any sharing can happen, check whether the share feature is allowed through `isShareLocked`. ```swift if let isShareLocked = ZoomVideoSDK.shareInstance()?.getShareHelper()?.isShareLocked(), !isShareLocked { // Continue with screen sharing } ``` ```objectivec if ([[[ZoomVideoSDK shareInstance] getShareHelper] isShareLocked]) { // Do something if share is locked } else { // Continue with screen sharing } ``` Once any user starts broadcasting during a session, use the `onUserShareStatusChanged` callback function of the `ZoomVideoSDKDelegate` to monitor screen share events (get notified when a user starts or stops sharing their screen). `ZoomVideoSDKDelegate` is located within `ZoomVideoSDK.xcframework`. Implement this delegate within your main target. The SDK calls the `onUserShareStatusChanged` function when a user starts or stops the broadcast in a session. Once the user starts sharing the screen, call the `subscribeWithView` method to start rendering the shared screen in the session. To stop rendering the screen, call the `unSubscribeWithView` method. ```swift public func onUserShareStatusChanged(_ helper: ZoomVideoSDKShareHelper?, user: ZoomVideoSDKUser?, shareAction: ZoomVideoSDKShareAction?) { // Get the current share status and handle your use case according to the new status let status = shareAction?.getShareStatus() switch (status) { case .none: return case .start: // User has begun sharing. // Retrieve the share canvas and subscribe it to your view shareAction?.getShareCanvas()?.subscribe(with: yourView, aspectMode: .original, andResolution: ._Auto) case .pause: return case .resume: return case .stop: return default: return } } ``` ```objectivec - (void)onUserShareStatusChanged:(ZoomVideoSDKShareHelper * _Nullable)helper user:(ZoomVideoSDKUser * _Nullable)user shareAction:(ZoomVideoSDKShareAction*_Nullable)shareAction { // Get the current share status and handle your use case according to the new status ZoomVideoSDKReceiveSharingStatus status = [shareAction getShareStatus]; switch (status) { case ZoomVideoSDKReceiveSharingStatus_None: break; // User has begun sharing. case ZoomVideoSDKReceiveSharingStatus_Start: { // Retrieve the share canvas and subscribe it to your view [[shareAction getShareCanvas] subscribeWithView:self.yourView aspectMode:ZoomVideoSDKVideoAspect_Original andResolution:ZoomVideoSDKVideoResolution_Auto]; break; } case ZoomVideoSDKReceiveSharingStatus_Pause: break; case ZoomVideoSDKReceiveSharingStatus_Resume: break; // User has stopped sharing. case ZoomVideoSDKReceiveSharingStatus_Stop: { // Retrieve the share canvas and unsubscribe it from your view [[shareAction getShareCanvas] unSubscribeWithView:self.yourView]; break; } } } ``` If the SDK fails to subscribe to a view, it triggers the `onShareCanvasSubscribeFailWithUser` callback. ```swift public func onShareCanvasSubscribeFailWithUser(_ user: ZoomVideoSDKUser?, view: UIView?, shareAction: ZoomVideoSDKShareAction?) { // Get the subscribe fail reason let reason = shareAction?.getSubscribeFailReason() } ``` ```objectivec - (void)onShareCanvasSubscribeFailWithUser:(ZoomVideoSDKUser *_Nullable)user view:(UIView *_Nullable)view shareAction:(ZoomVideoSDKShareAction*_Nullable)shareAction { // Get the subscribe fail reason ZoomVideoSDKSubscribeFailReason reason = [shareAction getSubscribeFailReason]; } ``` ### React to share content-size changes When the sharer resizes their window, rotates their device, or otherwise changes the content dimensions, the SDK fires `onShareContentSizeChanged`. Read the new size from the share action with `getShareSourceContentSize` and re-layout your container if the aspect ratio is now different. ```swift public func onShareContentSizeChanged(_ helper: ZoomVideoSDKShareHelper?, user: ZoomVideoSDKUser?, shareAction: ZoomVideoSDKShareAction?) { let size = shareAction?.getShareSourceContentSize() // Resize your container or share view to match the new aspect. } ``` ```objectivec - (void)onShareContentSizeChanged:(ZoomVideoSDKShareHelper * _Nullable)helper user:(ZoomVideoSDKUser * _Nullable)user shareAction:(ZoomVideoSDKShareAction*_Nullable)shareAction { CGSize size = [shareAction getShareSourceContentSize]; // Resize your container or share view to match the new aspect. } ``` ### Identify the share source A share may be a screen, a camera, or a pure-audio stream. Read `getShareType` on the share action to identify which, and skip the canvas subscription for a pure-audio share since it has no frames to render. ```swift switch shareAction?.getShareType() { case .normal: // Screen, UIView, or external source. break case .camera: // The user is sharing a camera as content. break case .pureAudio: // Audio-only share — no video frames, no canvas to subscribe. break case .none: break default: break } ``` ```objectivec switch ([shareAction getShareType]) { case ZoomVideoSDKShareType_Normal: // Screen, UIView, or external source. break; case ZoomVideoSDKShareType_Camera: // The user is sharing a camera as content. break; case ZoomVideoSDKShareType_PureAudio: // Audio-only share — no video frames, no canvas to subscribe. break; case ZoomVideoSDKShareType_None: break; } ``` The SDK also fires `onShareContentChanged` when a sharer switches between source types (for example, from a camera share to a screen share) without stopping the share. Treat it as a signal to refresh anything you cached based on share type. ## Share a UIView This tutorial demonstrates how to use the Video SDK to share the in-app screen (single `UIView`) of a user in a session. There are some network limitations with this method, but if the `UIView` is not complex, it can be used to share your view. ### Prerequisites - You have [integrated](/docs/video-sdk/ios/integrate/) the `ZoomVideoSDK.xcframework` into your project and implemented a basic [video session](/docs/video-sdk/ios/video/) feature. ### Import frameworks Import the `ZoomVideoSDK.xcframework` into your project similarly to how you've done previously, however, this time copy the `CptShare.xcframework` into your Xcode project folder and "Embed & Sign" it. ![Xcode Framework location.](/img/vsdk-ios-share.png) ### Start sharing the screen Use the `startShareWithView` function of the `ZoomVideoSDKShareHelper` to start sharing a `UIView` with the session. ```swift // Get the ZoomVideoSDKShareHelper to perform UIView sharing actions. if let shareHelper = ZoomVideoSDK.shareInstance()?.getShareHelper() { // Call startShareWithView to begin sharing. let returnValue = shareHelper.startShare(with: view) if returnValue == .Errors_Success { // Your view is now being shared. } else { // The view could not be shared. } } ``` ```objectivec // Get the ZoomVideoSDKShareHelper to perform UIView sharing actions. ZoomVideoSDKShareHelper *shareHelper = [[ZoomVideoSDK shareInstance] getShareHelper]; if (shareHelper) { // Call startShareWithView to begin sharing. ZoomVideoSDKERROR returnValue = [shareHelper startShareWithView:view]; if (returnValue == Errors_Success) { // Your view is now being shared. } else { // The view could not be shared. } } ``` Use the `subscribeWithView` method of the `ZoomVideoSDKVideoCanvas` class to subscribe to the `UIView` that is being shared and render it in the session. ```swift let user: ZoomVideoSDKUser // Get the specific user you want to subscribe to their share screen let shareActionList = user.getShareActionList() // The shareActionList can contain 0 to multiple shares coming from the selected user if let shareActionList = shareActionList, shareActionList.count > 0 { // Get the specific share to subscribe to, such as the first one let shareAction = shareActionList[0] // Get the render canvas helper object of the share action if let shareCanvas = shareAction.getShareCanvas() { // Sharing must be done on main thread Task(priority: .background) { // Set video aspect. let videoAspect = ZoomVideoSDKVideoAspect.panAndScan let resolution = ZoomVideoSDKVideoResolution.Auto // Render the share action's share stream shareCanvas.subscribe(with: view, aspectMode: videoAspect, andResolution: resolution) } } } ``` ```objectivec ZoomVideoSDKUser *user; // Assume this is already obtained NSArray *shareActionList = [user getShareActionList]; if (shareActionList != nil && shareActionList.count > 0) { ZoomVideoSDKShareAction *shareAction = shareActionList[0]; ZoomVideoSDKVideoCanvas *shareCanvas = [shareAction getShareCanvas]; if (shareCanvas != nil) { // Ensure UI operations happen on the main thread dispatch_async(dispatch_get_main_queue(), ^{ ZoomVideoSDKVideoAspect videoAspect = ZoomVideoSDKVideoAspect_PanAndScan; [shareCanvas subscribeWithView:view andAspectMode:videoAspect andResolution:ZoomVideoSDKVideoResolution_Auto]; }); } } ``` ### Stop sharing the screen Use the `stopShare` method of the `ZoomVideoSDKShareHelper` class to stop sharing the screen. ```swift // Get the ZoomVideoSDKShareHelper to perform UIView sharing actions. if let shareHelper = ZoomVideoSDK.shareInstance()?.getShareHelper() { // Call stopShare to stop sharing view. let returnValue = shareHelper.stopShare() if returnValue == .Errors_Success { // Your view has stopped being shared. } else { // Could not stop sharing view. } } ``` ```objectivec // Get the ZoomVideoSDKShareHelper to perform UIView sharing actions. ZoomVideoSDKShareHelper *shareHelper = [[ZoomVideoSDK shareInstance] getShareHelper]; if (shareHelper) { // Call stopShare to stop sharing view. ZoomVideoSDKERROR returnValue = [shareHelper stopShare]; if (returnValue == Errors_Success) { // Your view has stopped being shared. } else { // Could not stop sharing view. } } ``` ### Pause and resume your share To pause an ongoing share without ending the share session, call `pauseShare`. Viewers see a frozen frame until you call `resumeShare`. Both methods return a `ZoomVideoSDKError`. ```swift if let shareHelper = ZoomVideoSDK.shareInstance()?.getShareHelper() { // Pause the current share. shareHelper.pauseShare() // Later, resume it. shareHelper.resumeShare() } ``` ```objectivec ZoomVideoSDKShareHelper *shareHelper = [[ZoomVideoSDK shareInstance] getShareHelper]; if (shareHelper) { // Pause the current share. [shareHelper pauseShare]; // Later, resume it. [shareHelper resumeShare]; } ``` Receivers detect pause and resume through the `ZoomVideoSDKReceiveSharingStatus_Pause` and `ZoomVideoSDKReceiveSharingStatus_Resume` values on `onUserShareStatusChanged`. ### Check the local share state To drive your UI, for example to disable a **Share** button while the local user is already sharing or while another participant is, query the share helper's state. `isSharingOut`, `isScreenSharingOut`, and `isOtherSharing` each return a `BOOL`. ```swift if let shareHelper = ZoomVideoSDK.shareInstance()?.getShareHelper() { // The local user is currently sharing (any share type). let sharing = shareHelper.isSharingOut() // The local user is specifically sharing the screen. let sharingScreen = shareHelper.isScreenSharingOut() // Some other participant is sharing. let otherSharing = shareHelper.isOtherSharing() } ``` ```objectivec ZoomVideoSDKShareHelper *shareHelper = [[ZoomVideoSDK shareInstance] getShareHelper]; if (shareHelper) { BOOL sharing = [shareHelper isSharingOut]; BOOL sharingScreen = [shareHelper isScreenSharingOut]; BOOL otherSharing = [shareHelper isOtherSharing]; } ``` To let viewers choose between several simultaneous shares, see [Handle multiple screen shares](/docs/video-sdk/ios/share/handle-multiple-shares/). If broadcasting doesn't start or your extension doesn't appear in the picker, see [Troubleshooting](/docs/video-sdk/ios/share/troubleshooting/).