@videosdk.live/expo-ios-screen-share
Version:
Expo config plugin for iOS screen sharing with VideoSDK
170 lines (169 loc) • 8.66 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const config_plugins_1 = require("@expo/config-plugins");
const plist_1 = __importDefault(require("@expo/plist"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const withIosScreenCapture = (config, props) => {
config = withAppEntitlements(config, props);
config = withBroadcastEntitlements(config, props);
config = withInfoPlistRTC(config, props);
config = withBroadcastSources(config, props);
return config;
};
const withAppEntitlements = (config, props) => {
config = (0, config_plugins_1.withEntitlementsPlist)(config, (config) => {
const appGroupIdentifier = `group.${props.bundleId}.appgroup`;
config.modResults['com.apple.security.application-groups'] = [
appGroupIdentifier,
];
return config;
});
return config;
};
const withBroadcastEntitlements = (config, props) => {
return (0, config_plugins_1.withXcodeProject)(config, async (config) => {
const appGroupIdentifier = `group.${props.bundleId}.appgroup`;
const extensionName = props.extensionName || 'broadcast';
const extensionRootPath = path.join(config.modRequest.platformProjectRoot, extensionName);
const entitlementsPath = path.join(extensionRootPath, `${extensionName}.entitlements`);
const extensionEntitlements = {
'com.apple.security.application-groups': [appGroupIdentifier],
};
// create file
await fs.promises.mkdir(path.dirname(entitlementsPath), {
recursive: true,
});
await fs.promises.writeFile(entitlementsPath, plist_1.default.build(extensionEntitlements));
// add file to extension group
const proj = config.modResults;
const targetUuid = proj.findTargetKey(extensionName);
const groupUuid = proj.findPBXGroupKey({ name: extensionName });
proj.addFile(`${extensionName}.entitlements`, groupUuid, {
target: targetUuid,
lastKnownFileType: 'text.plist.entitlements',
});
// update build properties
proj.updateBuildProperty('CODE_SIGN_ENTITLEMENTS', `${extensionName}/${extensionName}.entitlements`, null, extensionName);
// Add Apple Team ID to build settings
proj.updateBuildProperty('DEVELOPMENT_TEAM', props.appleTeamId, null, extensionName);
return config;
});
};
const withInfoPlistRTC = (config, props) => {
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
const appGroupIdentifier = `group.${props.bundleId}.appgroup`;
const extensionName = props.extensionName || 'broadcast';
const extensionBundleIdentifier = `${props.bundleId}.${extensionName}`;
config.modResults['RTCAppGroupIdentifier'] = appGroupIdentifier;
config.modResults['RTCScreenSharingExtension'] = extensionBundleIdentifier;
// Always set the extension name in Info.plist for the native module to use
const fullExtensionBundleId = `${props.bundleId}.${extensionName}`;
config.modResults['RTCScreenSharingExtensionName'] = fullExtensionBundleId;
if (!config.modResults['UIBackgroundModes']) {
config.modResults['UIBackgroundModes'] = [];
}
config.modResults['UIBackgroundModes'].push('voip');
return config;
});
};
const withBroadcastSources = (config, props) => {
return (0, config_plugins_1.withXcodeProject)(config, async (config) => {
const appGroupIdentifier = `group.${props.bundleId}.appgroup`;
const extensionName = props.extensionName || 'broadcast';
const extensionRootPath = path.join(config.modRequest.platformProjectRoot, extensionName);
const iosRootPath = config.modRequest.platformProjectRoot; // Main iOS directory
// Create broadcast extension directory and copy files
await fs.promises.mkdir(extensionRootPath, { recursive: true });
await fs.promises.copyFile(path.join(__dirname, 'static', 'Atomic.swift'), path.join(extensionRootPath, 'Atomic.swift'));
await fs.promises.copyFile(path.join(__dirname, 'static', 'DarwinNotificationCenter.swift'), path.join(extensionRootPath, 'DarwinNotificationCenter.swift'));
// Override SampleHandler.swift initial template
await fs.promises.copyFile(path.join(__dirname, 'static', 'SampleHandler.swift'), path.join(extensionRootPath, 'SampleHandler.swift'));
await fs.promises.copyFile(path.join(__dirname, 'static', 'SampleUploader.swift'), path.join(extensionRootPath, 'SampleUploader.swift'));
await fs.promises.copyFile(path.join(__dirname, 'static', 'SocketConnection.swift'), path.join(extensionRootPath, 'SocketConnection.swift'));
// Update app group bundle id in SampleHandler code
const code = await fs.promises.readFile(path.join(extensionRootPath, 'SampleHandler.swift'), { encoding: 'utf-8' });
await fs.promises.writeFile(path.join(extensionRootPath, 'SampleHandler.swift'), code.replace('group.com.example.broadcast.appgroup', appGroupIdentifier));
// Add source files to broadcast extension
addSourceFiles(config.modResults, extensionRootPath, extensionName);
// Add bridge files to main app target with proper configuration
addBridgeFiles(config.modResults, config.modRequest.projectName);
return config;
});
};
const addSourceFiles = (proj, extensionRootPath, extensionName) => {
const targetUuid = proj.findTargetKey(extensionName);
const groupUuid = proj.findPBXGroupKey({ name: extensionName });
if (!targetUuid) {
return;
}
if (!groupUuid) {
return;
}
proj.addSourceFile('Atomic.swift', {
target: targetUuid,
}, groupUuid);
proj.addSourceFile('DarwinNotificationCenter.swift', {
target: targetUuid,
}, groupUuid);
proj.addSourceFile('SampleUploader.swift', {
target: targetUuid,
}, groupUuid);
proj.addSourceFile('SocketConnection.swift', {
target: targetUuid,
}, groupUuid);
};
const addBridgeFiles = (proj, projectName) => {
const mainTargetUuid = proj.getFirstTarget()?.uuid;
const mainGroupUuid = proj.getFirstProject().firstProject.mainGroup;
if (!mainTargetUuid) {
return;
}
// Ensure Swift version is set for both configurations
proj.updateBuildProperty('SWIFT_VERSION', '5.0', 'Debug', mainTargetUuid);
proj.updateBuildProperty('SWIFT_VERSION', '5.0', 'Release', mainTargetUuid);
// Enable Swift compilation for both configurations
proj.updateBuildProperty('SWIFT_INSTALL_OBJC_HEADER', 'YES', 'Debug', mainTargetUuid);
proj.updateBuildProperty('SWIFT_INSTALL_OBJC_HEADER', 'YES', 'Release', mainTargetUuid);
// Enable modules for Swift interoperability
proj.updateBuildProperty('CLANG_ENABLE_MODULES', 'YES', 'Debug', mainTargetUuid);
proj.updateBuildProperty('CLANG_ENABLE_MODULES', 'YES', 'Release', mainTargetUuid);
console.log(`✅ Added React Native bridge files to main target ${mainTargetUuid}`);
};
exports.default = withIosScreenCapture;