はじめに
インストール
Section titled “インストール”npm install @capgo/capacitor-twilio-voicenpx cap syncyarn add @capgo/capacitor-twilio-voicenpx cap syncpnpm add @capgo/capacitor-twilio-voicenpx cap syncbun add @capgo/capacitor-twilio-voicenpx cap syncTwilioアカウントと認証用のアクセストークンが必要です。アカウントをお持ちでない場合はTwilioでサインアップしてください。
プラットフォーム設定
Section titled “プラットフォーム設定”Info.plistに必要なパーミッションを追加します:
<key>NSMicrophoneUsageDescription</key><string>This app needs microphone access for voice calls</string>PushKitを使用した着信通話の場合:
- Xcodeの機能でプッシュ通知を有効化
- VoIPバックグラウンドモードを追加
- Twilioコンソールでvip証明書を設定
Android
Section titled “Android”AndroidManifest.xmlに必要なパーミッションを追加します:
<uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />プッシュ通知用にFirebaseを設定:
google-services.jsonをAndroidプロジェクトに追加- TwilioコンソールでFCMを設定
import { TwilioVoice } from '@capgo/capacitor-twilio-voice';
// Twilioで認証await TwilioVoice.login({ accessToken: 'your-twilio-access-token'});
// 通話を発信await TwilioVoice.makeCall({ to: '+1234567890'});
// 通話イベントをリスニングTwilioVoice.addListener('callConnected', (data) => { console.log('Call connected:', data.callSid);});
TwilioVoice.addListener('callDisconnected', (data) => { console.log('Call disconnected:', data.callSid);});
TwilioVoice.addListener('incomingCall', (data) => { console.log('Incoming call from:', data.from);
// 通話を承認 TwilioVoice.acceptCall({ callSid: data.callSid });
// または拒否 // TwilioVoice.rejectCall({ callSid: data.callSid });});
// 通話をミュート/ミュート解除await TwilioVoice.muteCall({ muted: true, callSid: 'current-call-sid'});
// スピーカーを切り替えawait TwilioVoice.setSpeaker({ enabled: true});
// 通話を終了await TwilioVoice.endCall({ callSid: 'current-call-sid'});
// ログアウトawait TwilioVoice.logout();APIリファレンス
Section titled “APIリファレンス”login(options)
Section titled “login(options)”login(options: { accessToken: string }) => Promise<void>アクセストークンを使用してTwilioで認証します。
| Param | Type |
|---|---|
options | { accessToken: string } |
logout()
Section titled “logout()”logout() => Promise<void>ユーザーセッションを終了し、通話状態をクリアします。
isLoggedIn()
Section titled “isLoggedIn()”isLoggedIn() => Promise<{ loggedIn: boolean }>現在の認証ステータスを確認します。
戻り値: Promise<{ loggedIn: boolean }>
makeCall(options)
Section titled “makeCall(options)”makeCall(options: { to: string; params?: Record<string, string> }) => Promise<{ callSid: string }>指定された番号への発信通話を開始します。
| Param | Type |
|---|---|
options | { to: string; params?: Record<string, string> } |
戻り値: Promise<{ callSid: string }>
acceptCall(options)
Section titled “acceptCall(options)”acceptCall(options: { callSid: string }) => Promise<void>着信通話を承認します。
| Param | Type |
|---|---|
options | { callSid: string } |
rejectCall(options)
Section titled “rejectCall(options)”rejectCall(options: { callSid: string }) => Promise<void>着信通話を拒否します。
| Param | Type |
|---|---|
options | { callSid: string } |
endCall(options?)
Section titled “endCall(options?)”endCall(options?: { callSid?: string }) => Promise<void>アクティブな通話を終了します。
| Param | Type |
|---|---|
options | { callSid?: string } (オプション) |
muteCall(options)
Section titled “muteCall(options)”muteCall(options: { muted: boolean; callSid?: string }) => Promise<void>通話音声をミュートまたはミュート解除します。
| Param | Type |
|---|---|
options | { muted: boolean; callSid?: string } |
setSpeaker(options)
Section titled “setSpeaker(options)”setSpeaker(options: { enabled: boolean }) => Promise<void>スピーカー出力を切り替えます。
| Param | Type |
|---|---|
options | { enabled: boolean } |
sendDigits(options)
Section titled “sendDigits(options)”sendDigits(options: { digits: string; callSid?: string }) => Promise<void>通話中にDTMFトーンを送信します。
| Param | Type |
|---|---|
options | { digits: string; callSid?: string } |
イベントリスナー
Section titled “イベントリスナー”利用可能なイベント
Section titled “利用可能なイベント”registered- Twilioへの登録に成功しましたunregistered- Twilioから登録解除されましたregistrationFailed- 登録に失敗しましたincomingCall- 着信通話を受信しましたcallConnected- 通話が正常に接続されましたcallDisconnected- 通話が切断されましたcallRinging- 発信通話が呼び出し中ですcallReconnecting- 通話が再接続中ですcallReconnected- 中断後に通話が再接続されましたqualityWarning- 通話品質の警告error- エラーが発生しました
// 着信通話を処理TwilioVoice.addListener('incomingCall', (data) => { console.log('Incoming call from:', data.from); console.log('Call SID:', data.callSid);
// 着信通話UIを表示 showIncomingCallScreen({ from: data.from, callSid: data.callSid });});
// 通話状態の変更を処理TwilioVoice.addListener('callConnected', (data) => { console.log('Call connected:', data.callSid); startCallTimer();});
TwilioVoice.addListener('callDisconnected', (data) => { console.log('Call ended:', data.callSid); stopCallTimer(); hideCallScreen();});
// 品質警告を処理TwilioVoice.addListener('qualityWarning', (data) => { console.warn('Call quality warning:', data.warning); showQualityWarning(data.warning);});
// エラーを処理TwilioVoice.addListener('error', (error) => { console.error('Twilio error:', error.message); handleError(error);});
// 完了したらリスナーを削除const listener = await TwilioVoice.addListener('callConnected', (data) => { console.log('Connected');});
// 後で...listener.remove();import { TwilioVoice } from '@capgo/capacitor-twilio-voice';
class VoiceCallService { private currentCallSid: string | null = null; private isMuted = false; private isSpeakerOn = false;
async initialize(accessToken: string) { try { // Twilioにログイン await TwilioVoice.login({ accessToken });
// ログインステータスを確認 const { loggedIn } = await TwilioVoice.isLoggedIn(); console.log('Login status:', loggedIn);
// イベントリスナーをセットアップ this.setupEventListeners();
} catch (error) { console.error('Failed to initialize:', error); } }
setupEventListeners() { // 登録イベント TwilioVoice.addListener('registered', () => { console.log('Successfully registered with Twilio'); });
TwilioVoice.addListener('registrationFailed', (error) => { console.error('Registration failed:', error); });
// 着信通話 TwilioVoice.addListener('incomingCall', async (data) => { console.log('Incoming call from:', data.from);
const accepted = await this.showIncomingCallDialog(data.from);
if (accepted) { await TwilioVoice.acceptCall({ callSid: data.callSid }); this.currentCallSid = data.callSid; } else { await TwilioVoice.rejectCall({ callSid: data.callSid }); } });
// 通話イベント TwilioVoice.addListener('callConnected', (data) => { console.log('Call connected'); this.currentCallSid = data.callSid; this.showCallScreen(); });
TwilioVoice.addListener('callDisconnected', () => { console.log('Call disconnected'); this.currentCallSid = null; this.hideCallScreen(); });
TwilioVoice.addListener('callRinging', () => { console.log('Call ringing...'); });
// 品質警告 TwilioVoice.addListener('qualityWarning', (data) => { console.warn('Call quality warning:', data.warning); this.showQualityIndicator(data.warning); }); }
async makeCall(phoneNumber: string) { try { const result = await TwilioVoice.makeCall({ to: phoneNumber, params: { // オプションのカスタムパラメータ customerId: 'customer-123' } });
this.currentCallSid = result.callSid; console.log('Call initiated:', result.callSid); } catch (error) { console.error('Failed to make call:', error); } }
async endCall() { if (this.currentCallSid) { await TwilioVoice.endCall({ callSid: this.currentCallSid }); this.currentCallSid = null; } }
async toggleMute() { this.isMuted = !this.isMuted; await TwilioVoice.muteCall({ muted: this.isMuted, callSid: this.currentCallSid || undefined }); }
async toggleSpeaker() { this.isSpeakerOn = !this.isSpeakerOn; await TwilioVoice.setSpeaker({ enabled: this.isSpeakerOn }); }
async sendDTMF(digits: string) { if (this.currentCallSid) { await TwilioVoice.sendDigits({ digits, callSid: this.currentCallSid }); } }
async logout() { await TwilioVoice.logout(); }
private async showIncomingCallDialog(from: string): Promise<boolean> { // ネイティブダイアログまたはカスタムUIを表示 return confirm(`Incoming call from ${from}`); }
private showCallScreen() { // 通話UIを表示 console.log('Showing call screen'); }
private hideCallScreen() { // 通話UIを非表示 console.log('Hiding call screen'); }
private showQualityIndicator(warning: string) { // 品質警告を表示 console.log('Quality warning:', warning); }}
// 使用方法const voiceService = new VoiceCallService();
// バックエンドからのアクセストークンで初期化const token = await fetchTwilioToken();await voiceService.initialize(token);
// 通話を発信await voiceService.makeCall('+1234567890');
// 通話を制御await voiceService.toggleMute();await voiceService.toggleSpeaker();await voiceService.sendDTMF('1');
// 通話を終了await voiceService.endCall();
// ログアウトawait voiceService.logout();ベストプラクティス
Section titled “ベストプラクティス”- アクセストークンはバックエンドサーバーから取得し、アプリに埋め込まない
- 長時間実行セッションのためにトークンリフレッシュロジックを実装する
- 再接続ロジックでネットワーク中断を処理する
- 通話状態の視覚的フィードバックを提供する
- プッシュ通知で実際のデバイスでテストする
- 適切なエラー処理を実装する
- コンポーネントがアンマウントされたらリスナーをクリーンアップする
- 通話を発信する前にマイクパーミッションをリクエストする
セキュリティに関する考慮事項
Section titled “セキュリティに関する考慮事項”- Twilio認証情報をアプリに保存しない
- バックエンドでアクセストークンを生成する
- トークンの有効期限とリフレッシュを実装する
- すべてのトークンリクエストにHTTPSを使用する
- サーバーサイドで着信通話を検証する
トラブルシューティング
Section titled “トラブルシューティング”通話が接続されない
Section titled “通話が接続されない”- アクセストークンが有効で期限切れでないことを確認
- ネットワーク接続を確認
- マイクパーミッションが付与されていることを確認
- Twilioアカウントが適切に設定されていることを確認
プッシュ通知が機能しない
Section titled “プッシュ通知が機能しない”- TwilioでPushKit/FCM証明書が設定されていることを確認
- デバイスがプッシュ通知に登録されていることを確認
- 本番証明書でテストする
- マイクパーミッションを確認
- スピーカー/Bluetooth設定を確認
- 実際のデバイスで音声ルーティングをテストする
発信者名表示 (CapacitorTwilioCallerName)
Section titled “発信者名表示 (CapacitorTwilioCallerName)”デフォルトでは、着信通話は発信者の電話番号またはクライアントIDを表示します。TwiMLバックエンドからCapacitorTwilioCallerNameパラメータを渡すことで、代わりにわかりやすい名前を表示するようにカスタマイズできます。
バックエンドのセットアップ
Section titled “バックエンドのセットアップ”<Client>ダイアルのTwiMLレスポンスを生成する際に、CapacitorTwilioCallerNameパラメータを追加します:
// Javaの例Parameter callerNameParam = new Parameter.Builder() .name("CapacitorTwilioCallerName") .value("John Doe") .build();
Client client = new Client.Builder(identity) .parameter(callerNameParam) .build();
Dial dial = new Dial.Builder() .client(client) .build();// Node.jsの例const VoiceResponse = require('twilio').twiml.VoiceResponse;
const response = new VoiceResponse();const dial = response.dial();dial.client({ name: 'CapacitorTwilioCallerName', value: 'John Doe'}, identity);- バックエンドが着信通話を受信すると、通話をルーティングするTwiMLを生成します
- 発信者の表示名を含む
CapacitorTwilioCallerNameパラメータを含めます - プラグインは自動的にこのパラメータを抽出し、以下に使用します:
- iOS CallKit着信通話画面
- Android着信通話通知
incomingCallイベントのfromフィールド- 通話ステータスの
pendingInvites配列
CapacitorTwilioCallerNameが提供されない場合、プラグインは発信者の電話番号またはクライアントIDにフォールバックします。