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

Commit c1d6c40

Browse files
Merge pull request #166 from twilio/3.x
Merge 3.x to master
2 parents bedbae6 + c597fe0 commit c1d6c40

File tree

11 files changed

+688
-148
lines changed

11 files changed

+688
-148
lines changed

Cartfile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github "twilio/twilio-voice-ios" >= 3.0.0
2+

Docs/migration-guide-2.x-3.x.md

+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
## 2.x to 3.x Migration Guide
2+
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.
3+
4+
1. [Making a Call](#making-a-call)
5+
2. [TVOCallInvite Changes](#tvocallinvite-changes)
6+
3. [Specifying a Media Region](#specifying-a-media-region)
7+
4. [TVOConnectOptions & TVOAcceptOptions](#tvoconnectoptions-and-tvoacceptoptions)
8+
5. [Media Establishment & Connectivity](#media-establishment-and-connectivity)
9+
6. [CallKit](#callkit)
10+
7. [Microphone Permission](#microphone-permission)
11+
12+
#### <a name="making-a-call"></a>Making a Call
13+
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:]`.
14+
15+
```.objc
16+
TVOCall *call = [TwilioVoice connectWithAccessToken:token delegate:self];
17+
```
18+
19+
#### <a name="tvocallinvite-changes"></a>TVOCallInvite Changes
20+
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.
21+
22+
```.objc
23+
- (void)pushRegistry:(PKPushRegistry *)registry
24+
didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
25+
forType:(NSString *)type {
26+
if (![TwilioVoice handleNotification:payload.dictionaryPayload delegate:self]) {
27+
// The push notification was not a Twilio Voice push notification.
28+
}
29+
}
30+
31+
#pragma mark - TVONotificationDelegate
32+
- (void)callInviteReceived:(TVOCallInvite *)callInvite {
33+
// Show notification to answer or reject call
34+
}
35+
36+
- (void)cancelledCallInviteReceived:(TVOCancelledCallInvite *)cancelledCallInvite {
37+
// Hide notification
38+
}
39+
```
40+
41+
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`.
42+
43+
In Voice iOS 2.X passing a `cancel` notification into `[TwilioVoice handleNotification:delegate:]` would not raise a callback in the following two cases:
44+
45+
- This callee accepted the call
46+
- This callee rejected the call
47+
48+
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:]`.
49+
50+
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.
51+
52+
#### <a name="specifying-a-media-region"></a>Specifying a media region
53+
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:
54+
55+
```.objc
56+
TVOConnectOptions *options = [TVOConnectOptions optionsWithAccessToken:accessToken
57+
block:^(TVOConnectOptionsBuilder *builder) {
58+
builder.region = region;
59+
}];
60+
61+
TVOAcceptOptions *options = [TVOAcceptOptions optionsWithCallInvite:callInvite
62+
block:^(TVOAcceptOptionsBuilder *builder) {
63+
builder.region = region;
64+
}];
65+
```
66+
67+
#### <a name="tvoconnectoptions-and-tvoacceptoptions"></a>TVOConnectOptions & TVOAcceptOptions
68+
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:
69+
70+
```.objc
71+
TVOConnectOptions *options = [TVOConnectOptions optionsWithAccessToken:accessToken
72+
block:^(TVOConnectOptionsBuilder *builder) {
73+
builder.params = params;
74+
}];
75+
76+
self.call = [TwilioVoice connectWithOptions:options delegate:self];
77+
```
78+
79+
A `TVOCallInvite` can also be accepted using `TVOAcceptOptions` as shown below:
80+
81+
```.objc
82+
TVOAcceptOptions *options = [TVOAcceptOptions optionsWithCallInvite:callInvite
83+
block:^(TVOAcceptOptionsBuilder *builder) {
84+
builder.region = region;
85+
}];
86+
87+
self.call = [callInvite acceptWithOptions:options delegate:self];
88+
```
89+
90+
#### <a name="media-establishment-and-connectivity"></a>Media Establishment & Connectivity
91+
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:
92+
93+
```.objc
94+
TVOIceOptions *iceOptions;
95+
96+
NSMutableArray *iceServers = [NSMutableArray array];
97+
TVOIceServer *iceServer1 = [[TVOIceServer alloc] initWithURLString:@"stun:global.stun.twilio.com:3478?transport=udp"
98+
username:@""
99+
password:@""];
100+
[iceServers addObject:iceServer];
101+
102+
TVOIceServer *iceServer2 = [[TVOIceServer alloc] initWithURLString:@"turn:global.turn.twilio.com:3478?transport=udp"
103+
username:@"TURN_USERNAME"
104+
password:@"TURN_PASSWORD"];
105+
[iceServers addObject:iceServer2];
106+
107+
iceOptions = [TVOIceOptions optionsWithBlock:^(TVOIceOptionsBuilder *builder) {
108+
builder.servers = iceServers;
109+
}];
110+
111+
// Specify ICE options in the builder
112+
TVOConnectOptions *options = [TVOConnectOptions optionsWithAccessToken:accessToken
113+
block:^(TVOConnectOptionsBuilder *builder) {
114+
builder.iceOptions = iceOptions;
115+
}];
116+
117+
TVOAcceptOptions *options = [TVOAcceptOptions optionsWithCallInvite:callInvite
118+
block:^(TVOAcceptOptionsBuilder *builder) {
119+
builder.iceOptions = iceOptions;
120+
}];
121+
```
122+
123+
#### <a name="callkit"></a>CallKit
124+
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.
125+
126+
An example of managing the `TVODefaultAudioDevice` while connecting a CallKit Call:
127+
128+
```.objc
129+
@property (nonatomic, strong) TVODefaultAudioDevice *audioDevice;
130+
131+
- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
132+
self.audioDevice.enabled = NO;
133+
self.audioDevice.block();
134+
135+
[self.callKitProvider reportOutgoingCallWithUUID:action.callUUID startedConnectingAtDate:[NSDate date]];
136+
137+
__weak typeof(self) weakSelf = self;
138+
[self performVoiceCallWithUUID:action.callUUID client:nil completion:^(BOOL success) {
139+
__strong typeof(self) strongSelf = weakSelf;
140+
if (success) {
141+
[strongSelf.callKitProvider reportOutgoingCallWithUUID:action.callUUID connectedAtDate:[NSDate date]];
142+
[action fulfill];
143+
} else {
144+
[action fail];
145+
}
146+
}];
147+
}
148+
149+
- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {
150+
self.audioDevice.enabled = YES;
151+
}
152+
153+
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
154+
self.audioDevice.enabled = NO;
155+
self.audioDevice.block();
156+
157+
[self performAnswerVoiceCallWithUUID:action.callUUID completion:^(BOOL success) {
158+
if (success) {
159+
[action fulfill];
160+
} else {
161+
[action fail];
162+
}
163+
}];
164+
165+
[action fulfill];
166+
}
167+
168+
- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
169+
// Disconnect or reject the call
170+
171+
self.audioDevice.enabled = YES;
172+
[action fulfill];
173+
}
174+
```
175+
176+
See [CallKit Example](https://github.com/twilio/voice-quickstart-objc/blob/3.x/ObjCVoiceCallKitQuickstart/ViewController.m) for the complete implementation.
177+
178+
#### <a name="microphone-permission"></a>Microphone Permission
179+
Unlike Voice iOS 2.X SDKs where microphone permission is not optional in Voice 3.X SDKs, the call will connect even when the microphone permission is denied or disabled by the user, and the SDK will play the remote audio. To ensure the microphone permission is enabled prior to making or accepting a call you can add the following to request the permission beforehand:
180+
181+
```
182+
- (void)makeCall {
183+
// User's microphone option
184+
BOOL microphoneEnabled = YES;
185+
186+
if (microphoneEnabled) {
187+
[self checkRecordPermission:^(BOOL permissionGranted) {
188+
if (!permissionGranted) {
189+
// The user might want to revisit the Privacy settings.
190+
} else {
191+
// Permission granted. Continue to make call.
192+
}
193+
}];
194+
} else {
195+
// Continue to make call without microphone.
196+
}
197+
}
198+
199+
- (void)checkRecordPermission:(void(^)(BOOL permissionGranted))completion {
200+
AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
201+
switch (permissionStatus) {
202+
case AVAudioSessionRecordPermissionGranted:
203+
// Record permission already granted.
204+
completion(YES);
205+
break;
206+
case AVAudioSessionRecordPermissionDenied:
207+
// Record permission denied.
208+
completion(NO);
209+
break;
210+
case AVAudioSessionRecordPermissionUndetermined:
211+
{
212+
// Requesting record permission.
213+
// Optional: pop up app dialog to let the users know if they want to request.
214+
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
215+
completion(granted);
216+
}];
217+
break;
218+
}
219+
default:
220+
completion(NO);
221+
break;
222+
}
223+
}
224+
```

Docs/new-features-3.0.md

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
## SDK 3.0 New Features
2+
Voice iOS 3.0 has a number of new features listed below:
3+
4+
1. [WebRTC](#webrtc)
5+
2. [Custom Parameters](#custom-parameters)
6+
3. [Call Ringing APIs](#call-ringing-apis)
7+
4. [Media Stats](#media-stats)
8+
5. [Audio Device APIs](#audio-device-apis)
9+
* [Default Audio Device](#default-audio-device)
10+
* [Custom Audio Device](#custom-audio-device)
11+
6. [Preferred Audio Codec](#preferred-audio-codec)
12+
13+
#### <a name="webrtc"></a>WebRTC
14+
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.
15+
16+
#### <a name="custom-parameters"></a>Custom Parameters
17+
Custom Parameters is now supported in `TVOCallInvite`. `TVOCallInvite.customParamaters` returns a `NSDictionary` of custom parameters sent from the caller side to the callee.
18+
19+
Pass custom parameters in TwiML:
20+
21+
```.xml
22+
<?xml version="1.0" encoding="UTF-8"?>
23+
<Response>
24+
<Dial callerId="client:alice">
25+
<Client>
26+
<Identity>bob</Identity>
27+
<Parameter name="caller_first_name" value="alice" />
28+
<Parameter name="caller_last_name" value="smith" />
29+
</Client>
30+
</Dial>
31+
</Response>
32+
```
33+
34+
`callInvite.customParameters` returns a dictionary of key-value pairs passed in the TwiML:
35+
36+
```.objc
37+
{
38+
"caller_first_name" = "alice";
39+
"caller_last_name" = "smith";
40+
}
41+
```
42+
43+
#### <a name="call-ringing-apis"></a>Call Ringing APIs
44+
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`.
45+
46+
These changes are added as follows:
47+
48+
```.objc
49+
// TVOCall.h
50+
typedef NS_ENUM(NSUInteger, TVOCallState) {
51+
TVOCallStateConnecting = 0, ///< The Call is connecting.
52+
TVOCallStateRinging, ///< The Call is ringing.
53+
TVOCallStateConnected, ///< The Call is connected.
54+
TVOCallStateDisconnected ///< The Call is disconnected.
55+
};
56+
57+
// TVOCallDelegate.h
58+
@protocol TVOCallDelegate
59+
60+
@optional
61+
- (void)callDidStartRinging:(nonnull TVOCall *)call;
62+
63+
@end
64+
```
65+
66+
#### <a name="media-stats"></a>Media Stats
67+
In Voice iOS 3.0 SDK you can now access media stats in a Call using the `[TVOCall getStatsWithBlock:]` method.
68+
69+
```.objc
70+
[call getStatsWithBlock:^(NSArray<TVOStatsReport *> *statsReports) {
71+
for (TVOStatsReport *report in statsReports) {
72+
NSArray *localAudioTracks = report.localAudioTrackStats;
73+
TVOLocalAudioTrackStats *localAudioTrackStats = localAudioTracks[0];
74+
NSArray *remoteAudioTracks = report.remoteAudioTrackStats;
75+
TVORemoteAudioTrackStats *remoteAudioTrackStats = remoteAudioTracks[0];
76+
77+
NSLog(@"Local Audio Track - audio level: %lu, packets sent: %lu", localAudioTrackStats.audioLevel, localAudioTrackStats.packetsSent);
78+
NSLog(@"Remote Audio Track - audio level: %lu, packets received: %lu", remoteAudioTrackStats.audioLevel, remoteAudioTrackStats.packetsReceived);
79+
}
80+
}
81+
```
82+
### <a name="audio-device-apis"></a>Audio Device APIs
83+
84+
#### <a name="default-audio-device"></a>Default Audio Device
85+
In Voice iOS 3.0 SDK, `TVODefaultAudioDevice` is used as the default device for rendering and capturing audio.
86+
87+
An example of using `TVODefaultAudioDevice` to change the audio route from receiver to the speaker in a live call:
88+
89+
```.objc
90+
// The Voice SDK uses TVODefaultAudioDevice by default.
91+
// ... connect to a Call. The `TVODefaultAudioDevice` is configured to route audio to the receiver by default.
92+
93+
TVODefaultAudioDevice *audioDevice = (TVODefaultAudioDevice *)TwilioVoice.audioDevice;
94+
95+
audioDevice.block = ^ {
96+
// We will execute `kDefaultAVAudioSessionConfigurationBlock` first.
97+
kDefaultAVAudioSessionConfigurationBlock();
98+
99+
if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]) {
100+
NSLog(@"AVAudiosession overrideOutputAudioPort %@",error);
101+
}
102+
};
103+
audioDevice.block();
104+
```
105+
106+
#### <a name="custom-audio-device"></a>Custom Audio Device
107+
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`.
108+
109+
Connecting to a Call using the `AVAudioSessionCategoryPlayback` category:
110+
111+
```.objc
112+
id<TVOAudioDevice> audioDevice = [TVODefaultAudioDevice audioDeviceWithBlock:^ {
113+
114+
// Execute the `kDefaultAVAudioSessionConfigurationBlock` first.
115+
kDefaultAVAudioSessionConfigurationBlock();
116+
117+
// Overwrite the category to `playback`
118+
AVAudioSession *session = [AVAudioSession sharedInstance];
119+
NSError *error = nil;
120+
if (![session setCategory:AVAudioSessionCategoryPlayback
121+
mode:AVAudioSessionModeVoiceChat
122+
options:AVAudioSessionCategoryOptionAllowBluetooth
123+
error:&error]) {
124+
NSLog(@"AVAudioSession setCategory:options:mode:error: %@",error);
125+
}
126+
}];
127+
128+
TwilioVoice.audioDevice = audioDevice;
129+
130+
TVOCall *call = [TwilioVoice connectWithOptions:connectOptions delegate:self];
131+
```
132+
133+
### <a name="preferred-audio-codec"></a>Preferred Audio Codec
134+
In Voice iOS 3.0, you can provide your preferred audio codec in the `TVOConnectOptions` and the `TVOAcceptOptions`.
135+
The only audio codec supported by our mobile infrastructure is currently PCMU. Opus is not currently available on our mobile infrastructure. However it will become available in Q1 of 2019. At that point the default audio codec for all 3.0 mobile clients will be Opus. To always use PCMU as the negotiated audio codec instead you can add it as the first codec in the `preferAudioCodecs` list.
136+
137+
```.objc
138+
#import "TVOAudioCodec.h"
139+
140+
TVOConnectOptions *options = [TVOConnectOptions optionsWithAccessToken:accessToken
141+
block:^(TVOConnectOptionsBuilder *builder) {
142+
builder.preferredAudioCodecs = @[ [TVOOpusCodec new], [TVOPcmuCodec new] ];
143+
}];
144+
145+
self.call = [TwilioVoice connectWithOptions:options delegate:self];
146+
```

ObjCVoiceCallKitQuickstart/AppDelegate.m

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ @interface AppDelegate ()
1515
@implementation AppDelegate
1616

1717
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
18-
NSLog(@"Twilio Voice Version: %@", [TwilioVoice version]);
18+
NSLog(@"Twilio Voice Version: %@", [TwilioVoice sdkVersion]);
1919
return YES;
2020
}
2121

ObjCVoiceCallKitQuickstart/Assets.xcassets/AppIcon.appiconset/Contents.json

+5
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@
139139
"idiom" : "ipad",
140140
"filename" : "Icon-83.5@2x.png",
141141
"scale" : "2x"
142+
},
143+
{
144+
"idiom" : "ios-marketing",
145+
"size" : "1024x1024",
146+
"scale" : "1x"
142147
}
143148
],
144149
"info" : {

0 commit comments

Comments
 (0)