Skip to content
This repository has been archived by the owner on Jan 18, 2020. It is now read-only.

Add New Features section in README #131

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 161 additions & 125 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

## Get started with Voice on iOS:
* [Quickstart](#quickstart) - Run the quickstart app
* [New Features](#new-features) - New features in 3.X
* [Migration Guide](#migration-guide) - Migrating from 2.X to 3.X
* [Access Tokens](#access-tokens) - Using access tokens
* [Managing Audio Interruptions](#managing-audio-interruptions) - Managing audio interruptions
Expand Down Expand Up @@ -225,10 +226,155 @@ There are number of techniques you can use to ensure that access token expiry is
- Retain the access token along with the timestamp of when it was requested so you can verify ahead of time whether the token has already expired based on the `time-to-live` being used by your server.
- Prefetch the access token whenever the `UIApplication`, or `UIViewController` associated with an outgoing call is created.

## New Features
Voice iOS 3.X has a number of new features listed below:

1. [WebRTC](#webrtc)
2. [Custom Parameters](#custom-parameters)
3. [Call Ringing APIs](#call-ringing-apis)
4. [Media Stats](#media-stats)
5. [Audio Device APIs](#audio-device-apis)
* [Default Audio Device](#default-audio-device)
* [Custom Audio Device](#custom-audio-device)

#### <a name="webrtc"></a>WebRTC
The SDK is built using Chromium WebRTC for iOS. This ensures that over time developers will get the best real-time media streaming capabilities available for iOS. Additionally, upgrades to new versions of Chromium WebRTC will happen without changing the public API whenever possible.

#### <a name="custom-parameters"></a>Custom Parameters
Custom Parameters is now supported in `TVOCallInvite`. `TVOCallInvite.customParamaters` returns a `NSDictionary` of custom parameters sent from the caller side to the callee.

Pass custom parameters in TwiML:

```.xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial callerId="client:alice">
<Client>
<Identity>bob</Identity>
<Parameter name="caller_first_name" value="alice" />
<Parameter name="caller_last_name" value="smith" />
</Client>
</Dial>
</Response>
```

`callInvite.customParameters` returns a dictionary of key-value pairs passed in the TwiML:

```.objc
{
"caller_first_name" = "alice";
"caller_last_name" = "smith";
}
```

#### <a name="call-ringing-apis"></a>Call Ringing APIs
Ringing is now provided as a call state. The delegate method `callDidStartRinging:` corresponding to this state transition is called once before the `callDidConnect:` method when the callee is being alerted of a Call. The behavior of this callback is determined by the `answerOnBridge` flag provided in the `Dial` verb of your TwiML application associated with this client. If the `answerOnBridge` flag is `false`, which is the default, the `callDidConnect:` callback will be called immediately after `callDidStartRinging:`. If the `answerOnBridge` flag is `true`, this will cause the `callDidConnect:` method being called only after the Call is answered. See [answerOnBridge](https://www.twilio.com/docs/voice/twiml/dial#answeronbridge) for more details on how to use it with the `Dial` TwiML verb. If the TwiML response contains a `Say` verb, then the `callDidConnect:` method will be called immediately after `callDidStartRinging:` is called, irrespective of the value of `answerOnBridge` being set to `true` or `false`.

These changes are added as follows:

```.objc
// TVOCall.h
typedef NS_ENUM(NSUInteger, TVOCallState) {
TVOCallStateConnecting = 0, ///< The Call is connecting.
TVOCallStateRinging, ///< The Call is ringing.
TVOCallStateConnected, ///< The Call is connected.
TVOCallStateDisconnected ///< The Call is disconnected.
};

// TVOCallDelegate.h
@protocol TVOCallDelegate

@optional
- (void)callDidStartRinging:(nonnull TVOCall *)call;

@end
```

#### <a name="media-stats"></a>Media Stats
In Voice iOS 3.X SDK you can now access media stats in a Call using the `[TVOCall getStatsWithBlock:]` method.

```.objc
[call getStatsWithBlock:^(NSArray<TVOStatsReport *> *statsReports) {
for (TVOStatsReport *report in statsReports) {
NSArray *localAudioTracks = report.localAudioTrackStats;
TVOLocalAudioTrackStats *localAudioTrackStats = localAudioTracks[0];
NSArray *remoteAudioTracks = report.remoteAudioTrackStats;
TVORemoteAudioTrackStats *remoteAudioTrackStats = remoteAudioTracks[0];

NSLog(@"Local Audio Track - audio level: %lu, packets sent: %lu", localAudioTrackStats.audioLevel, localAudioTrackStats.packetsSent);
NSLog(@"Remote Audio Track - audio level: %lu, packets received: %lu", remoteAudioTrackStats.audioLevel, remoteAudioTrackStats.packetsReceived);
}
}
```
### <a name="audio-device-apis"></a>Audio Device APIs

#### <a name="default-audio-device"></a>Default Audio Device
In Voice iOS 3.X SDK, `TVODefaultAudioDevice` is used as the default device for rendering and capturing audio.

An example of using `TVODefaultAudioDevice` to change the audio route from receiver to the speaker in a live call:

```.objc
// The Voice SDK uses TVODefaultAudioDevice by default.
// ... connect to a Call. The `TVODefaultAudioDevice` is configured to route audio to the receiver by default.

TVODefaultAudioDevice *audioDevice = (TVODefaultAudioDevice *)TwilioVoice.audioDevice;

audioDevice.block = ^ {
// We will execute `kDefaultAVAudioSessionConfigurationBlock` first.
kDefaultAVAudioSessionConfigurationBlock();

if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]) {
NSLog(@"AVAudiosession overrideOutputAudioPort %@",error);
}
};
audioDevice.block();
```

#### <a name="custom-audio-device"></a>Custom Audio Device
The `TVOAudioDevice` protocol gives you the ability to replace `TVODefaultAudioDevice`. By implementing the `TVOAudioDevice` protocol, you can write your own audio capturer to feed audio samples to the Voice SDK and an audio renderer to receive the remote audio samples. For example, you could integrate with `ReplayKit2` and capture application audio for broadcast or play music using `AVAssetReader`.

Connecting to a Call using the `AVAudioSessionCategoryPlayback` category:

```.objc
id<TVOAudioDevice> audioDevice = [TVODefaultAudioDevice audioDeviceWithBlock:^ {

// Execute the `kDefaultAVAudioSessionConfigurationBlock` first.
kDefaultAVAudioSessionConfigurationBlock();

// Overwrite the category to `playback`
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
if (![session setCategory:AVAudioSessionCategoryPlayback
mode:AVAudioSessionModeVoiceChat
options:AVAudioSessionCategoryOptionAllowBluetooth
error:&error]) {
NSLog(@"AVAudioSession setCategory:options:mode:error: %@",error);
}
}];

TwilioVoice.audioDevice = audioDevice;

TVOCall *call = [TwilioVoice connectWithOptions:connectOptions delegate:self];
```

## Migration Guide
This section describes API changes and additions to ease migration from Voice iOS 2.X to Voice iOS 3.X. Each section provides code snippets to assist in transitioning to the new API.
This section describes API or behavioral changes when upgrading from Voice iOS 2.X to Voice iOS 3.X. Each section provides code snippets to assist in transitioning to the new API.

1. [Making a Call](#making-a-call)
2. [TVOCallInvite Changes](#tvocallinvite-changes)
3. [Specifying a Media Region](#specifying-a-media-region)
4. [TVOConnectOptions & TVOAcceptOptions](#tvoconnectoptions-and-tvoacceptoptions)
5. [Media Establishment & Connectivity](#media-establishment-and-connectivity)
6. [CallKit](#callkit)

### TVOCallInvite Changes
#### <a name="making-a-call"></a>Making a Call
In Voice iOS 3.X, the API to make a call has changed from `[TwilioVoice call:params:delegate:]` to `[TwilioVoice connectWithAccessToken:delegate]` or `[TwilioVoice connectWithOptions:delegate:]`.

```.objc
TVOCall *call = [TwilioVoice connectWithAccessToken:token delegate:self];
```

#### <a name="tvocallinvite-changes"></a>TVOCallInvite Changes
In Voice iOS 3.X, the `notificationError:` delegate method is removed from the `TVONotificationDelegate` protocol and the `[TwilioVoice handleNotification:]` method no longer raises errors via this method if an invalid notification is provided, instead a `BOOL` value is returned when `[TwilioVoice handleNotification:]` is called. The returned value is `YES` when the provided data resulted in a `TVOCallInvite` or `TVOCancelledCallInvite` received in the `TVONotificationDelegate` methods. If `NO` is returned it means the data provided was not a Twilio Voice push notification.

```.objc
Expand All @@ -252,7 +398,16 @@ didReceiveIncomingPushWithPayload:(PKPushPayload *)payload

The `TVOCallInvite` has an `accept()` and `reject()` method. `TVOCallInviteState` has been removed from the `TVOCallInvite` in favor of distinguishing between call invites and call invite cancellations with discrete stateless objects. While the `TVOCancelledCallInvite` simply provides the `to`, `from`, and `callSid` fields also available in the `TVOCallInvite`. The property `callSid` can be used to associate a `TVOCallInvite` with a `TVOCancelledCallInvite`.

### Specifying a media region
In Voice iOS 2.X passing a `cancel` notification into `[TwilioVoice handleNotification:delegate:]` would not raise a callback in the following two cases:

- This callee accepted the call
- This callee rejected the call

However, in Voice iOS 3.X passing a `cancel` notification payload into `[TwilioVoice handleNotification:delegate:]` will always result in a callback. A callback is raised whenever a valid notification is provided to `[TwilioVoice handleNotification:delegate:]`.

Note that Twilio will send a `cancel` notification to every registered device of the identity that accepts or rejects a call, even the device that accepted or rejected the call.

#### <a name="specifying-a-media-region"></a>Specifying a media region
Previously, a media region could be specified via `[TwilioVoice setRegion:]`. Now this configuration can be provided as part of `TVOConnectOptions` or `TVOAcceptOptions` as shown below:

```.objc
Expand All @@ -267,7 +422,7 @@ TVOAcceptOptions *options = [TVOAcceptOptions optionsWithCallInvite:callInvite
}];
```

### TVOConnectOptions & TVOAcceptOptions
#### <a name="tvoconnectoptions-and-tvoacceptoptions"></a>TVOConnectOptions & TVOAcceptOptions
To support configurability upon making or accepting a call, new classes have been added. Create a `TVOConnectOptions` object and make configurations via the `TVOConnectOptionsBuilder` in the `block`. Once `TVOConnectOptions` is created it can be provided when connecting a Call as shown below:

```.objc
Expand All @@ -290,30 +445,7 @@ TVOAcceptOptions *options = [TVOAcceptOptions optionsWithCallInvite:callInvite
self.call = [callInvite acceptWithOptions:options delegate:self];
```

### Ringing
Ringing is now provided as a call state. The delegate method `callDidStartRinging:` corresponding to this state transition is called once before the `callDidConnect:` method when the callee is being alerted of a Call. The behavior of this callback is determined by the `answerOnBridge` flag provided in the `Dial` verb of your TwiML application associated with this client. If the `answerOnBridge` flag is `false`, which is the default, the `callDidConnect:` callback will be called immediately after `callDidStartRinging:`. If the `answerOnBridge` flag is `true`, this will cause the `callDidConnect:` method being called only after the Call is answered. See [answerOnBridge](https://www.twilio.com/docs/voice/twiml/dial#answeronbridge) for more details on how to use it with the `Dial` TwiML verb. If the TwiML response contains a `Say` verb, then the `callDidConnect:` method will be called immediately after `callDidStartRinging:` is called, irrespective of the value of `answerOnBridge` being set to `true` or `false`.

These changes are added as follows:

```.objc
// TVOCall.h
typedef NS_ENUM(NSUInteger, TVOCallState) {
TVOCallStateConnecting = 0, ///< The Call is connecting.
TVOCallStateRinging, ///< The Call is ringing.
TVOCallStateConnected, ///< The Call is connected.
TVOCallStateDisconnected ///< The Call is disconnected.
};

// TVOCallDelegate.h
@protocol TVOCallDelegate

@optional
- (void)callDidStartRinging:(nonnull TVOCall *)call;

@end
```

### Media Establishment & Connectivity
#### <a name="media-establishment-and-connectivity"></a>Media Establishment & Connectivity
The Voice iOS 3.X SDK uses WebRTC. The exchange of real-time media requires the use of Interactive Connectivity Establishment(ICE) to establish a media connection between the client and the media server. In some network environments where network access is restricted it may be necessary to provide ICE servers to establish a media connection. We reccomend using the [Network Traversal Service (NTS)](https://www.twilio.com/stun-turn) to obtain ICE servers. ICE servers can be provided when making or accepting a call by passing them into `TVOConnectOptions` or `TVOAcceptOptions` in the following way:

```.objc
Expand Down Expand Up @@ -346,32 +478,7 @@ TVOAcceptOptions *options = [TVOAcceptOptions optionsWithCallInvite:callInvite
}];
```

### Audio Device
The Voice iOS 3.X SDK introduces audio device APIs.

#### TVODefaultAudioDevide
In Voice iOS 3.X SDK, `TVODefaultAudioDevice` is used as the default device for rendering and capturing audio.

An example of using `TVODefaultAudioDevice` to change the audio route from receiver to the speaker in a live call:

```.objc
// The Voice SDK uses TVODefaultAudioDevice by default.
// ... connect to a Call. The `TVODefaultAudioDevice` is configured to route audio to the receiver by default.

TVODefaultAudioDevice *audioDevice = (TVODefaultAudioDevice *)TwilioVoice.audioDevice;

audioDevice.block = ^ {
// We will execute `kDefaultAVAudioSessionConfigurationBlock` first.
kDefaultAVAudioSessionConfigurationBlock();

if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]) {
NSLog(@"AVAudiosession overrideOutputAudioPort %@",error);
}
};
audioDevice.block();
```

#### CallKit
#### <a name="callkit"></a>CallKit
The Voice iOS 3.X SDK deprecates the `CallKitIntegration` category from `TwilioVoice` in favor of a new property called `TVODefaultAudioDevice.enabled`. This property provides developers with a mechanism to enable or disable the activation of the audio device prior to connecting to a Call or to stop or start the audio device while you are already connected to a Call. A Call can now be connected without activating the audio device by setting `TVODefaultAudioDevice.enabled` to `NO` and can be enabled during the lifecycle of the Call by setting `TVODefaultAudioDevice.enabled` to `YES`. The default value is `YES`. This API change was made to ensure full compatibility with CallKit as well as supporting other use cases where developers may need to disable the audio device during a call.

An example of managing the `TVODefaultAudioDevice` while connecting a CallKit Call:
Expand Down Expand Up @@ -426,77 +533,6 @@ An example of managing the `TVODefaultAudioDevice` while connecting a CallKit Ca

See [CallKit Example](https://github.com/twilio/voice-quickstart-objc/blob/3.x/ObjCVoiceCallKitQuickstart/ViewController.m) for the complete implementation.

#### TVOAudioDevice
The `TVOAudioDevice` protocol gives you the ability to replace `TVODefaultAudioDevice`. By implementing the `TVOAudioDevice` protocol, you can write your own audio capturer to feed audio samples to the Voice SDK and an audio renderer to receive the remote audio samples. For example, you could integrate with `ReplayKit2` and capture application audio for broadcast or play music using `AVAssetReader`.

Connecting to a Call using the `AVAudioSessionCategoryPlayback` category:

```.objc
id<TVOAudioDevice> audioDevice = [TVODefaultAudioDevice audioDeviceWithBlock:^ {

// Execute the `kDefaultAVAudioSessionConfigurationBlock` first.
kDefaultAVAudioSessionConfigurationBlock();

// Overwrite the category to `playback`
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
if (![session setCategory:AVAudioSessionCategoryPlayback
mode:AVAudioSessionModeVoiceChat
options:AVAudioSessionCategoryOptionAllowBluetooth
error:&error]) {
NSLog(@"AVAudioSession setCategory:options:mode:error: %@",error);
}
}];

TwilioVoice.audioDevice = audioDevice;

TVOCall *call = [TwilioVoice connectWithOptions:connectOptions delegate:self];
```

### Custom Parameters
Custom Parameters is now supported in `TVOCallInvite`. `TVOCallInvite.customParamaters` returns a `NSDictionary` of custom parameters sent from the caller side to the callee.

Pass custom parameters in TwiML:

```.xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<Dial callerId="client:alice">
<Client>
<Identity>bob</Identity>
<Parameter name="caller_first_name" value="alice" />
<Parameter name="caller_last_name" value="smith" />
</Client>
</Dial>
</Response>
```

`callInvite.customParameters` returns a dictionary of key-value pairs passed in the TwiML:

```.objc
{
"caller_first_name" = "alice";
"caller_last_name" = "smith";
}
```

### Media Stats
In Voice iOS 3.X SDK you can now access media stats in a Call using the `[TVOCall getStatsWithBlock:]` method.

```.objc
[call getStatsWithBlock:^(NSArray<TVOStatsReport *> *statsReports) {
for (TVOStatsReport *report in statsReports) {
NSArray *localAudioTracks = report.localAudioTrackStats;
TVOLocalAudioTrackStats *localAudioTrackStats = localAudioTracks[0];
NSArray *remoteAudioTracks = report.remoteAudioTrackStats;
TVORemoteAudioTrackStats *remoteAudioTrackStats = remoteAudioTracks[0];

NSLog(@"Local Audio Track - audio level: %lu, packets sent: %lu", localAudioTrackStats.audioLevel, localAudioTrackStats.packetsSent);
NSLog(@"Remote Audio Track - audio level: %lu, packets received: %lu", remoteAudioTrackStats.audioLevel, remoteAudioTrackStats.packetsReceived);
}
}
```

## Managing Audio Interruptions
Different versions of iOS deal with **AVAudioSession** interruptions sightly differently. This section documents how the Programmable Voice iOS SDK manages audio interruptions and resumes call audio after the interruption ends. There are currently some cases that the SDK cannot resume call audio automatically because iOS does not provide the necessary notifications to indicate that the interruption has ended.

Expand Down