コンテンツへスキップ

Getting Started with RealtimeKit

このコンテンツはまだあなたの言語で利用できません。

This guide will walk you through integrating the Capacitor RealtimeKit plugin to add video conferencing powered by Cloudflare Calls to your application.

Installation

Install the plugin using npm:

Terminal window
npm install @capgo/capacitor-realtimekit
npx cap sync

Dependencies

This plugin uses the Cloudflare RealtimeKit SDK:

  • iOS: RealtimeKitCoreiOS (automatically installed via Swift Package Manager)
  • Android: com.cloudflare.realtimekit:ui-android version 0.2.2

Customizing Android RealtimeKit Version

In your app’s build.gradle:

buildscript {
ext {
realtimekitUiVersion = '0.2.2' // or your desired version
}
}

iOS Configuration

Add the following to your app’s Info.plist:

<key>NSCameraUsageDescription</key>
<string>We need camera access for video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need microphone access for audio calls</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need photo library access to share images</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>We need Bluetooth access for audio routing</string>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>voip</string>
<string>fetch</string>
<string>remote-notification</string>
</array>

Android Configuration

Add the following permissions to your AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />

Basic Usage

Import the Plugin

import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit';

Initialize the Plugin

async function initializeRealtimeKit() {
try {
await CapacitorRealtimekit.initialize();
console.log('RealtimeKit initialized');
} catch (error) {
console.error('Failed to initialize RealtimeKit:', error);
}
}

Start a Meeting

async function startMeeting(meetingUrl: string) {
try {
await CapacitorRealtimekit.startMeeting({
url: meetingUrl
});
console.log('Meeting started');
} catch (error) {
console.error('Failed to start meeting:', error);
}
}

Complete Example

Here’s a comprehensive video conferencing service:

import { CapacitorRealtimekit } from '@capgo/capacitor-realtimekit';
export interface MeetingConfig {
url: string;
displayName?: string;
audioEnabled?: boolean;
videoEnabled?: boolean;
}
export class VideoConferenceService {
private isInitialized = false;
private currentMeetingUrl: string | null = null;
async initialize(): Promise<boolean> {
if (this.isInitialized) {
console.log('RealtimeKit already initialized');
return true;
}
try {
await CapacitorRealtimekit.initialize();
this.isInitialized = true;
console.log('RealtimeKit initialized successfully');
return true;
} catch (error) {
console.error('Failed to initialize RealtimeKit:', error);
return false;
}
}
async startMeeting(config: MeetingConfig): Promise<void> {
if (!this.isInitialized) {
const initialized = await this.initialize();
if (!initialized) {
throw new Error('Failed to initialize RealtimeKit');
}
}
try {
await CapacitorRealtimekit.startMeeting({
url: config.url
});
this.currentMeetingUrl = config.url;
console.log('Meeting started:', config.url);
} catch (error) {
console.error('Failed to start meeting:', error);
throw error;
}
}
async joinMeeting(meetingUrl: string, displayName?: string): Promise<void> {
const config: MeetingConfig = {
url: meetingUrl,
displayName: displayName
};
await this.startMeeting(config);
}
getCurrentMeetingUrl(): string | null {
return this.currentMeetingUrl;
}
isInMeeting(): boolean {
return this.currentMeetingUrl !== null;
}
async getPluginVersion(): Promise<string> {
try {
const result = await CapacitorRealtimekit.getPluginVersion();
return result.version;
} catch (error) {
console.error('Failed to get plugin version:', error);
return 'unknown';
}
}
}
// Usage
const videoService = new VideoConferenceService();
// Initialize on app start
await videoService.initialize();
// Start a meeting
await videoService.startMeeting({
url: 'https://your-cloudflare-calls-url.com/meeting/123'
});
// Join an existing meeting
await videoService.joinMeeting(
'https://your-cloudflare-calls-url.com/meeting/456',
'John Doe'
);

Cloudflare Calls Setup

To use this plugin, you need to set up Cloudflare Calls:

1. Create Cloudflare Account

Sign up at Cloudflare if you don’t have an account.

2. Enable Calls API

  1. Navigate to your Cloudflare dashboard
  2. Go to Calls section
  3. Enable the Calls API
  4. Get your API credentials

3. Create Meeting URLs

You need a backend service to create meeting URLs. Example using Cloudflare Workers:

// Cloudflare Worker example
export default {
async fetch(request: Request): Promise<Response> {
const { pathname } = new URL(request.url);
if (pathname === '/create-meeting') {
const meetingId = generateMeetingId();
const meetingUrl = `https://your-app.calls.cloudflare.com/${meetingId}`;
return new Response(JSON.stringify({
meetingId,
meetingUrl
}), {
headers: { 'Content-Type': 'application/json' }
});
}
return new Response('Not found', { status: 404 });
}
};
function generateMeetingId(): string {
return Math.random().toString(36).substring(2, 15);
}

Advanced Usage

Meeting with Custom Configuration

class MeetingManager {
private videoService: VideoConferenceService;
constructor() {
this.videoService = new VideoConferenceService();
}
async createAndJoinMeeting(userName: string): Promise<string> {
// Initialize if needed
await this.videoService.initialize();
// Call your backend to create a meeting
const meetingUrl = await this.createMeetingOnBackend();
// Join the meeting
await this.videoService.joinMeeting(meetingUrl, userName);
return meetingUrl;
}
async createMeetingOnBackend(): Promise<string> {
const response = await fetch('https://your-api.com/create-meeting', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
const data = await response.json();
return data.meetingUrl;
}
async shareMeetingLink(meetingUrl: string) {
// Use Capacitor Share API
if ('share' in navigator) {
await (navigator as any).share({
title: 'Join my meeting',
text: 'Join me for a video call',
url: meetingUrl
});
}
}
}

Handling Permissions

import { Capacitor } from '@capacitor/core';
async function checkAndRequestPermissions(): Promise<boolean> {
if (Capacitor.getPlatform() === 'web') {
// Request browser permissions
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: true
});
// Stop the stream, we just needed permission
stream.getTracks().forEach(track => track.stop());
return true;
} catch (error) {
console.error('Permission denied:', error);
return false;
}
}
// On native platforms, permissions are requested automatically
return true;
}
// Use before starting a meeting
const hasPermissions = await checkAndRequestPermissions();
if (hasPermissions) {
await videoService.startMeeting({ url: meetingUrl });
}

React Integration

import { useEffect, useState } from 'react';
import { VideoConferenceService } from './VideoConferenceService';
function VideoCallComponent() {
const [videoService] = useState(() => new VideoConferenceService());
const [isReady, setIsReady] = useState(false);
useEffect(() => {
videoService.initialize().then(setIsReady);
}, []);
const joinMeeting = async (meetingUrl: string) => {
if (!isReady) {
console.error('Video service not ready');
return;
}
try {
await videoService.joinMeeting(meetingUrl, 'User Name');
} catch (error) {
console.error('Failed to join meeting:', error);
}
};
return (
<div>
<button
onClick={() => joinMeeting('https://your-meeting-url')}
disabled={!isReady}
>
Join Meeting
</button>
</div>
);
}

Vue Integration

import { ref, onMounted } from 'vue';
import { VideoConferenceService } from './VideoConferenceService';
export default {
setup() {
const videoService = new VideoConferenceService();
const isReady = ref(false);
onMounted(async () => {
isReady.value = await videoService.initialize();
});
const joinMeeting = async (meetingUrl: string) => {
if (!isReady.value) {
console.error('Video service not ready');
return;
}
try {
await videoService.joinMeeting(meetingUrl, 'User Name');
} catch (error) {
console.error('Failed to join meeting:', error);
}
};
return {
isReady,
joinMeeting
};
}
};

Best Practices

  1. Initialize Early: Initialize RealtimeKit when your app starts
  2. Handle Errors: Always wrap calls in try-catch blocks
  3. Request Permissions: Ensure camera/microphone permissions before starting
  4. Test Network: Check internet connectivity before joining
  5. Background Audio: Configure background modes for iOS
  6. User Experience: Show loading states during initialization
  7. Clean URLs: Validate meeting URLs before use

Common Issues

Meeting Not Starting

async function troubleshootMeeting(meetingUrl: string) {
// Check initialization
const version = await videoService.getPluginVersion();
console.log('Plugin version:', version);
// Verify URL format
if (!meetingUrl.startsWith('https://')) {
console.error('Invalid meeting URL, must use HTTPS');
return;
}
// Try starting meeting
try {
await videoService.startMeeting({ url: meetingUrl });
} catch (error) {
console.error('Meeting failed:', error);
}
}

Permission Denied

On iOS, ensure Info.plist has all required usage descriptions. On Android, verify AndroidManifest.xml permissions are present.

Audio Issues on iOS

Ensure background audio mode is configured in Info.plist:

<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>voip</string>
</array>

Next Steps