|
| 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 | +``` |
0 commit comments