Documentation

Full Device Screen Sharing

By default, the UserView React Native SDK shares only your app's screen. With full device screen sharing, agents can see the entire device screen, including other apps, the home screen, and system UI.

On iOS this uses Apple's Broadcast Upload Extension (ReplayKit); on Android it uses the MediaProjection API. Both need a little native setup, described below.

Physical devices only
Full device screen sharing only works on physical devices. It is not supported on the iOS Simulator or Android emulators.

Full device screen sharing must also be enabled in the UserView dashboard (admin interface) in addition to the steps below. If it is disabled there, agents will not see the full device option during sessions.

iOS setup

Full device capture on iOS runs in a Broadcast Upload Extension — a separate native target in your app's Xcode project (under ios/). It sends frames to your app through an App Group, and the SDK relays them to the agent.

1. Create the Broadcast Upload Extension target

Create the extension target first — the Podfile step below references it by name, so it must already exist in the Xcode project before you run pod install.

  1. Open ios/YourApp.xcworkspace in Xcode
  2. Go to File > New > Target
  3. Select Broadcast Upload Extension
  4. Uncheck "Include UI Extension" — a UI extension adds a setup screen whose default handler never completes, so the broadcast would never start
  5. Name it (e.g., YourAppBroadcast) — you'll reference this exact name in the Podfile next
  6. Note the extension's bundle identifier (Xcode prefixes it with your app's, e.g. com.yourcompany.yourapp.YourAppBroadcast) — you'll need it in step 6

2. Add the extension dependency

In your app's ios/Podfile, add the broadcast subspec to the extension target, declared as a top-level target (not nested inside your app target). Do not pin a version — it follows the UpscopeSDK version that @upscopeio/react-native-sdk already requires:

target 'YourAppBroadcast' do
  pod 'UpscopeSDK/BroadcastExtension'
end

If your app's targets are configured with use_frameworks!, add it to this target too so the extension's linkage matches its host. Then run pod install from the ios/ directory.

3. Configure App Groups

Both your main app and the extension need to share data through an App Group:

  1. Select your main app target > Signing & Capabilities > + Capability > App Groups
  2. Add a group identifier (e.g., group.com.yourcompany.yourapp)
  3. Select your extension target > Signing & Capabilities > + Capability > App Groups
  4. Add the same group identifier

4. Configure the extension's Info.plist

Add the App Group identifier to the extension's Info.plist:

<key>UpscopeAppGroupId</key>
<string>group.com.yourcompany.yourapp</string>

5. Implement the extension

Replace the contents of the extension's SampleHandler.swift with:

import UpscopeBroadcastExtension

class SampleHandler: UpscopeSampleHandler {}

All frame capture and forwarding is handled by UpscopeSampleHandler.

6. Configure the SDK

Pass the App Group and the extension's bundle identifier when initializing the SDK. broadcastExtensionBundleId must exactly match the extension target's bundle identifier from step 1 — it is not the App Group id:

import Upscope from '@upscopeio/react-native-sdk';

Upscope.initialize({
  apiKey: 'YOUR_API_KEY',
  broadcastAppGroupId: 'group.com.yourcompany.yourapp',
  broadcastExtensionBundleId: 'com.yourcompany.yourapp.YourAppBroadcast',
});

If you do not set broadcastAppGroupId, the SDK will not advertise full device support on iOS, and agent requests for it are declined automatically.

By default, an agent's full device request goes straight to the system permission prompt. To show your own confirmation UI first, set customFullDeviceRequestUI: true in the config and handle incoming requests — see Responding to full device requests below. With the flag set, requests stall silently unless a listener calls respondToFullDeviceRequest.

Android setup

Add the screen-capture permissions to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />

The SDK already declares the capture service and activity. It detects these permissions at runtime — if they are missing, full device screen sharing is disabled regardless of the dashboard setting. No further configuration is required; Android shows the system screen-capture permission dialog when sharing starts.

Responding to full device requests

By default the SDK proceeds directly to the system permission prompt when an agent requests full device sharing. To gate the request behind your own UI, set customFullDeviceRequestUI: true in the config — the SDK then routes requests to your JavaScript code instead. You must listen for fullDeviceRequest and respond, or requests will never proceed:

import { useEffect } from 'react';
import Upscope from '@upscopeio/react-native-sdk';

useEffect(() => {
  const subs = [
    Upscope.addListener('fullDeviceRequest', ({ requestId, agentName }) => {
      // Optionally show your own confirmation UI here (agentName is the
      // requesting agent's display name, or null), then respond:
      Upscope.respondToFullDeviceRequest(requestId, true); // false to decline
    }),

    Upscope.addListener('fullDeviceSharingStateChanged', ({ state }) => {
      console.log('Full device sharing:', state); // 'active' | 'pendingRequest' | 'inactive'
    }),
  ];

  return () => subs.forEach((s) => s.remove());
}, []);

After you accept, iOS shows the system broadcast picker (the user taps Start Broadcast) and Android shows the screen-capture dialog (the user taps Start now). End sharing programmatically at any time:

Upscope.stopFullDeviceSharing();

The user can also stop sharing from iOS Control Center or the Android capture notification; the SDK reports this via fullDeviceSharingStateChanged.

Responding to control requests

Remote control requests follow the same pattern, but are opt-in: set customControlRequestUI: true in the config to replace the native control request prompt. The SDK then emits controlRequest instead of showing the prompt; respond with respondToControlRequest to grant or decline:

import { useEffect } from 'react';
import Upscope from '@upscopeio/react-native-sdk';

useEffect(() => {
  const subs = [
    Upscope.addListener('controlRequest', ({ requestId, agentName }) => {
      // Optionally show your own confirmation UI here (agentName is the
      // requesting agent's display name, or null), then respond:
      Upscope.respondToControlRequest(requestId, true); // false to decline
    }),

    Upscope.addListener('remoteControlStateChanged', ({ state }) => {
      console.log('Remote control:', state); // 'active' | 'pendingRequest' | 'inactive'
    }),
  ];

  return () => subs.forEach((s) => s.remove());
}, []);

Revoke control programmatically at any time with Upscope.stopRemoteControl(); the session continues and only the agent's ability to interact stops.

Limitations

  • Works on physical devices only (not the iOS Simulator or Android emulators)
  • Element masking is not available in full device mode (the SDK cannot inspect views outside your app)
  • Remote control only works within your app, not on the home screen or other apps
  • Drawing annotations are not displayed in full device mode
  • The user must explicitly start the broadcast/capture via the system prompt — it cannot be started programmatically