Skip to main content

Permissions Management

Proper permission handling is essential for video streaming functionality. This guide covers camera, microphone, and storage permissions across all supported platforms, including best practices for user experience and compliance.

Required Permissions

Core Permissions

social.plus Video SDK requires the following permissions for full functionality:
PermissionPurposeRequired ForPlatforms
CameraVideo capture and broadcastingBroadcastingAll
MicrophoneAudio captureBroadcastingAll
StorageSave recordings, cache thumbnailsRecording, CachingMobile
NetworkStream data transmissionAll featuresAll
NotificationsStream alerts and updatesPush notificationsMobile, Web

Platform-Specific Permissions

iOS Permission Requirements

Info.plist Configuration:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to broadcast live streams and record videos</string>

<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access for live streaming audio and video recording</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to save and share videos</string>

<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs permission to save videos to your photo library</string>

<!-- For background streaming -->
<key>UIBackgroundModes</key>
<array>
    <string>background-audio</string>
    <string>background-processing</string>
</array>
Swift Permission Handling:
import AVFoundation
import Photos

class PermissionManager {
    
    func requestCameraPermission(completion: @escaping (Bool) -> Void) {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized:
            completion(true)
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { granted in
                DispatchQueue.main.async {
                    completion(granted)
                }
            }
        case .denied, .restricted:
            completion(false)
        @unknown default:
            completion(false)
        }
    }
    
    func requestMicrophonePermission(completion: @escaping (Bool) -> Void) {
        switch AVCaptureDevice.authorizationStatus(for: .audio) {
        case .authorized:
            completion(true)
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .audio) { granted in
                DispatchQueue.main.async {
                    completion(granted)
                }
            }
        case .denied, .restricted:
            completion(false)
        @unknown default:
            completion(false)
        }
    }
    
    func requestAllPermissions(completion: @escaping (Bool, Bool) -> Void) {
        requestCameraPermission { cameraGranted in
            self.requestMicrophonePermission { microphoneGranted in
                completion(cameraGranted, microphoneGranted)
            }
        }
    }
}

Permission Flow Best Practices

1. Progressive Permission Requests

class PermissionFlowManager {
    async initiatePermissionFlow(): Promise<boolean> {
        // Step 1: Explain why permissions are needed
        const userConsent = await this.showPermissionExplanation();
        if (!userConsent) return false;
        
        // Step 2: Request permissions one by one
        const cameraGranted = await this.requestCameraWithContext();
        if (!cameraGranted) return false;
        
        const micPermanted = await this.requestMicrophoneWithContext();
        if (!micPermanted) return false;
        
        // Step 3: Confirm success and guide next steps
        await this.showPermissionSuccess();
        return true;
    }
    
    private async showPermissionExplanation(): Promise<boolean> {
        return new Promise((resolve) => {
            const modal = this.createModal({
                title: 'Camera & Microphone Access',
                message: 'To broadcast live streams, we need access to your camera and microphone. Your privacy is important - we only use these during active streaming.',
                buttons: [
                    { text: 'Not Now', action: () => resolve(false) },
                    { text: 'Continue', action: () => resolve(true), primary: true }
                ]
            });
            modal.show();
        });
    }
    
    private async requestCameraWithContext(): Promise<boolean> {
        try {
            const granted = await this.requestCameraPermission();
            if (!granted) {
                await this.showCameraPermissionHelp();
            }
            return granted;
        } catch (error) {
            console.error('Camera permission error:', error);
            return false;
        }
    }
}

2. Permission State Management

interface PermissionState {
    camera: 'granted' | 'denied' | 'unknown' | 'blocked';
    microphone: 'granted' | 'denied' | 'unknown' | 'blocked';
    notifications: 'granted' | 'denied' | 'unknown';
    lastChecked: Date;
    userExplained: boolean;
}

class PermissionStateManager {
    private state: PermissionState = {
        camera: 'unknown',
        microphone: 'unknown',
        notifications: 'unknown',
        lastChecked: new Date(0),
        userExplained: false
    };
    
    async updatePermissionState(): Promise<PermissionState> {
        const [camera, microphone, notifications] = await Promise.all([
            this.checkCameraPermission(),
            this.checkMicrophonePermission(),
            this.checkNotificationPermission()
        ]);
        
        this.state = {
            camera: this.mapPermissionStatus(camera),
            microphone: this.mapPermissionStatus(microphone),
            notifications: this.mapPermissionStatus(notifications),
            lastChecked: new Date(),
            userExplained: this.state.userExplained
        };
        
        this.saveStateToStorage();
        return this.state;
    }
    
    canBroadcast(): boolean {
        return this.state.camera === 'granted' && this.state.microphone === 'granted';
    }
    
    canReceiveNotifications(): boolean {
        return this.state.notifications === 'granted';
    }
    
    needsPermissionRequest(): boolean {
        const hasUnknown = Object.values(this.state).includes('unknown');
        const isStale = Date.now() - this.state.lastChecked.getTime() > 24 * 60 * 60 * 1000; // 24 hours
        
        return hasUnknown || isStale;
    }
}

Error Handling and Recovery

Permission Error Types

enum PermissionErrorType {
    USER_DENIED = 'user_denied',
    SYSTEM_BLOCKED = 'system_blocked',
    HARDWARE_UNAVAILABLE = 'hardware_unavailable',
    ALREADY_IN_USE = 'already_in_use',
    UNKNOWN_ERROR = 'unknown_error'
}

class PermissionErrorHandler {
    handlePermissionError(error: any, permissionType: string): PermissionErrorType {
        // Web API errors
        if (error instanceof DOMException) {
            switch (error.name) {
                case 'NotAllowedError':
                    return PermissionErrorType.USER_DENIED;
                case 'NotFoundError':
                    return PermissionErrorType.HARDWARE_UNAVAILABLE;
                case 'NotReadableError':
                    return PermissionErrorType.ALREADY_IN_USE;
                case 'OverconstrainedError':
                    return PermissionErrorType.SYSTEM_BLOCKED;
                default:
                    return PermissionErrorType.UNKNOWN_ERROR;
            }
        }
        
        // Mobile platform errors
        if (typeof error === 'string') {
            if (error.includes('denied')) return PermissionErrorType.USER_DENIED;
            if (error.includes('blocked')) return PermissionErrorType.SYSTEM_BLOCKED;
        }
        
        return PermissionErrorType.UNKNOWN_ERROR;
    }
    
    getErrorMessage(errorType: PermissionErrorType, permissionType: string): string {
        const messages = {
            [PermissionErrorType.USER_DENIED]: `${permissionType} access was denied. Please allow access in your browser settings.`,
            [PermissionErrorType.SYSTEM_BLOCKED]: `${permissionType} access is blocked by your system. Please check your privacy settings.`,
            [PermissionErrorType.HARDWARE_UNAVAILABLE]: `${permissionType} hardware is not available on this device.`,
            [PermissionErrorType.ALREADY_IN_USE]: `${permissionType} is currently being used by another application.`,
            [PermissionErrorType.UNKNOWN_ERROR]: `An unknown error occurred while requesting ${permissionType} access.`
        };
        
        return messages[errorType] || messages[PermissionErrorType.UNKNOWN_ERROR];
    }
    
    getRecoveryActions(errorType: PermissionErrorType): string[] {
        const actions = {
            [PermissionErrorType.USER_DENIED]: [
                'Click the camera/microphone icon in your browser\'s address bar',
                'Select "Allow" for camera and microphone permissions',
                'Refresh the page and try again'
            ],
            [PermissionErrorType.SYSTEM_BLOCKED]: [
                'Open your system preferences',
                'Navigate to Privacy & Security settings',
                'Enable camera and microphone access for your browser'
            ],
            [PermissionErrorType.HARDWARE_UNAVAILABLE]: [
                'Check that your camera and microphone are connected',
                'Try using a different device',
                'Contact support if the issue persists'
            ],
            [PermissionErrorType.ALREADY_IN_USE]: [
                'Close other applications using the camera/microphone',
                'Restart your browser',
                'Try again'
            ]
        };
        
        return actions[errorType] || ['Please try again or contact support'];
    }
}

User Experience Best Practices

1. Permission Request Timing

class PermissionUXManager {
    async requestPermissionsWithOptimalTiming() {
        // Don't request on app start - wait for user intent
        await this.waitForUserIntent();
        
        // Provide context before requesting
        await this.showFeatureExplanation();
        
        // Request permissions when needed
        return this.requestPermissions();
    }
    
    private async waitForUserIntent(): Promise<void> {
        return new Promise((resolve) => {
            // Wait for user to tap "Start Broadcasting" or similar
            this.onUserActionToStartBroadcasting(() => resolve());
        });
    }
    
    private async showFeatureExplanation(): Promise<void> {
        const modal = {
            title: '📹 Ready to Go Live?',
            message: 'To start broadcasting, we\'ll need access to your camera and microphone. This helps you share your content with your audience!',
            animation: 'camera-preview', // Show animated camera preview
            primaryButton: 'Allow Access',
            secondaryButton: 'Maybe Later'
        };
        
        return this.showModal(modal);
    }
}

2. Graceful Degradation

class GracefulPermissionHandler {
    async initializeWithAvailablePermissions() {
        const permissions = await this.checkAllPermissions();
        
        const config = {
            features: {
                videoBroadcasting: permissions.camera && permissions.microphone,
                audioBroadcasting: !permissions.camera && permissions.microphone,
                viewerMode: true, // Always available
                chat: true,
                notifications: permissions.notifications
            },
            ui: {
                showBroadcastButton: permissions.camera || permissions.microphone,
                showPermissionPrompts: !permissions.camera || !permissions.microphone,
                enablePushNotifications: permissions.notifications
            }
        };
        
        return this.initializeSDK(config);
    }
    
    createAdaptiveUI(permissions: PermissionState) {
        const components = [];
        
        if (permissions.camera && permissions.microphone) {
            components.push('FullBroadcastControls');
        } else if (permissions.microphone) {
            components.push('AudioOnlyControls');
        } else {
            components.push('ViewerOnlyControls');
        }
        
        if (!permissions.camera || !permissions.microphone) {
            components.push('PermissionBanner');
        }
        
        return components;
    }
}

Compliance and Privacy

1. Privacy Compliance

class PrivacyComplianceManager {
    async ensurePrivacyCompliance() {
        // GDPR compliance
        if (this.isEUUser()) {
            await this.showGDPRConsent();
        }
        
        // CCPA compliance
        if (this.isCaliforniaUser()) {
            await this.showCCPANotice();
        }
        
        // Data usage transparency
        await this.explainDataUsage();
    }
    
    private async explainDataUsage() {
        const notice = {
            title: 'Your Privacy Matters',
            content: [
                '🔒 Camera and microphone data is only used during active streaming',
                '🚫 We never record or store your camera/microphone data without permission',
                '⏱️ Recordings are automatically deleted after 30 days unless you save them',
                '🛡️ All streaming data is encrypted in transit and at rest'
            ],
            actions: [
                { label: 'View Privacy Policy', action: this.openPrivacyPolicy },
                { label: 'Continue', action: this.proceedWithPermissions }
            ]
        };
        
        return this.showPrivacyNotice(notice);
    }
    
    async handleDataDeletion(userId: string) {
        // Handle user data deletion requests
        await this.deleteUserStreams(userId);
        await this.clearUserPreferences(userId);
        await this.removeUserFromAnalytics(userId);
    }
}

Next Steps

Now that you understand permission management, explore these related topics: For permission-related troubleshooting, see our Platform Issues Guide.