title: Twilio incoming call is not working in iOS
body: <p>I am working on Twilio programmable voice SDK. I integrated it with the help of <a href="https://www.twilio.com/docs/voice/sdks/ios" rel="nofollow noreferrer">documentation</a> and <a href="https://github.com/twilio/video-quickstart-ios" rel="nofollow noreferrer">GitHub SDK</a>. I can make a call from iOS to any number. It works fine.</p>
  <p>The issue is on receiving a call on Twilio number. I did everything that was mentioned in Twilio installation documentation and GitHub SDK, but its not working.</p>
  <p>here is the code:</p>
  <pre><code>@import PushKit;
  @import CallKit;
  @import TwilioVoice;
  @import UserNotifications;
  static NSString  * kAccessToken = @&quot;&quot;;
   NSString * phoneNumber = @&quot;&quot;;
  NSString * newToken = @&quot;&quot;;
  static NSString *const kTwimlParamTo = @&quot;to&quot;;
  
  static NSInteger const kRegistrationTTLInDays = 365;
  
  NSString * const kCachedDeviceToken = @&quot;CachedDeviceToken&quot;;
  NSString * const kCachedBindingTime = @&quot;CachedBindingTime&quot;;
  
  
  @interface RCTCallPackageModule () &lt;TVONotificationDelegate, TVOCallDelegate, CXProviderDelegate, UITextFieldDelegate, AVAudioPlayerDelegate , PushKitEventDelegate, PKPushRegistryDelegate&gt;
  @property (nonatomic, weak) id&lt;PushKitEventDelegate&gt; pushKitEventDelegate;
  
  @property (nonatomic, strong) void(^incomingPushCompletionCallback)(void);
  @property (nonatomic, strong) void(^callKitCompletionCallback)(BOOL);
  @property (nonatomic, strong) TVODefaultAudioDevice *audioDevice;
  @property (nonatomic, strong) NSMutableDictionary *activeCallInvites;
  @property (nonatomic, strong) NSMutableDictionary *activeCalls;
  
  // activeCall represents the last connected call
  @property (nonatomic, strong) TVOCall *activeCall;
  
  @property (nonatomic, strong) CXProvider *callKitProvider;
  @property (nonatomic, strong) CXCallController *callKitCallController;
  @property (nonatomic, assign) BOOL userInitiatedDisconnect;
  
  @property (nonatomic, assign) BOOL playCustomRingback;
  @property (nonatomic, strong) AVAudioPlayer *ringtonePlayer;
  
  
  @end
  
  
  
  
  
  
  
  //#import &lt;React/RCTLog.h&gt;
  
  @implementation RCTCallPackageModule
  
  
  
  -(NSString *)fetchAccessToken {
   NSString *accessToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:@&quot;http://54.172.240.200:4000/accessToken&quot;]
                            encoding:NSUTF8StringEncoding
                             error:nil];
  
   return accessToken;
  }
  
  -(void) mainIntializerFunction {
   
    self.pushKitEventDelegate = self;
  
    
    self.callKitCallController = [[CXCallController alloc] init];
  //    fetchData();
    /* Please note that the designated initializer `[CXProviderConfiguration initWithLocalizedName:]` has been deprecated on iOS 14. */
    CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:@&quot;Voice Quickstart&quot;];
    configuration.maximumCallGroups = 1;
    configuration.maximumCallsPerCallGroup = 1;
    
    self.callKitProvider = [[CXProvider alloc] initWithConfiguration:configuration];
    [self.callKitProvider setDelegate:self queue:nil];
    
   
    self.audioDevice = [TVODefaultAudioDevice audioDevice];
    TwilioVoiceSDK.audioDevice = self.audioDevice;
  
    
    self.activeCallInvites = [NSMutableDictionary dictionary];
    self.activeCalls = [NSMutableDictionary dictionary];
    
    
    self.playCustomRingback = NO;
    
    [self MainFunctionToCall];   }  
  - (void)dealloc {
      if (self.callKitProvider) {
          [self.callKitProvider invalidate];
      } }
  -(void) MainFunctionToCall {
    if (self.activeCall != nil) {
        self.userInitiatedDisconnect = YES;
        [self performEndCallActionWithUUID:self.activeCall.uuid];
    } else {
        NSUUID *uuid = [NSUUID UUID];
        NSString *handle = @&quot;Voice Bot&quot;;
        
        [self checkRecordPermission:^(BOOL permissionGranted) {
                [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
                                      completionHandler:^(BOOL granted, NSError * _Nullable error) {
                                          // Enable or disable features based on authorization.
                                      }];
  //              [[UIApplication sharedApplication] registerForRemoteNotifications];
                [self performStartCallActionWithUUID:uuid handle:handle];
            
        }];
      
    }
    
  }
  
  
  // performEndCallActiveUUID
  
  - (void)performEndCallActionWithUUID:(NSUUID *)uuid {
      CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid];
      CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction];
  
      [self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
          if (error) {
              NSLog(@&quot;EndCallAction transaction request failed: %@&quot;, [error localizedDescription]);
          }
          else {
            
          }
      }];
  }
  
  
  // checkRecordPermission
  
  - (void)checkRecordPermission:(void(^)(BOOL permissionGranted))completion {
      AVAudioSessionRecordPermission permissionStatus = [[AVAudioSession sharedInstance] recordPermission];
      switch (permissionStatus) {
          case AVAudioSessionRecordPermissionGranted:
              // Record permission already granted.
              completion(YES);
              break;
          case AVAudioSessionRecordPermissionDenied:
              // Record permission denied.
              completion(NO);
              break;
          case AVAudioSessionRecordPermissionUndetermined:
          {
              
              [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
                  completion(granted);
              }];
              break;
          }
          default:
              completion(NO);
              break;
      }
  }
  
  #pragma mark - CallKit Actions
  - (void)performStartCallActionWithUUID:(NSUUID *)uuid handle:(NSString *)handle {
      if (uuid == nil || handle == nil) {
          return;
      }
  
  
      CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
      CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:callHandle];
      CXTransaction *transaction = [[CXTransaction alloc] initWithAction:startCallAction];
  
      [self.callKitCallController requestTransaction:transaction completion:^(NSError *error) {
          if (error) {
              NSLog(@&quot;StartCallAction transaction request failed: %@&quot;, [error localizedDescription]);
          } else {
           
  
              CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
              callUpdate.remoteHandle = callHandle;
              callUpdate.supportsDTMF = YES;
              callUpdate.supportsHolding = YES;
              callUpdate.supportsGrouping = NO;
              callUpdate.supportsUngrouping = NO;
              callUpdate.hasVideo = NO;
  
              [self.callKitProvider reportCallWithUUID:uuid updated:callUpdate];
          }
      }];
  }
  
  #pragma mark - PushKitEventDelegate
  - (void)credentialsUpdated:(PKPushCredentials *)credentials {
      NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
      if ([self registrationRequired] || ![cachedDeviceToken isEqualToData:credentials.token]) {
          cachedDeviceToken = credentials.token;
          
          
          [TwilioVoiceSDK registerWithAccessToken:kAccessToken
                                      deviceToken:cachedDeviceToken
                                       completion:^(NSError *error) {
               if (error) {
                 
               } else {
             
                   
                   // Save the device token after successfully registered.
                   [[NSUserDefaults standardUserDefaults] setObject:cachedDeviceToken forKey:kCachedDeviceToken];
                   
                   
                   [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
               }
          }];
      }
  }
  
  - (BOOL)registrationRequired {
      BOOL registrationRequired = YES;
      NSDate *lastBindingCreated = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedBindingTime];
      
      if (lastBindingCreated) {
          NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
          
          // Register upon half of the TTL
          dayComponent.day = kRegistrationTTLInDays / 2;
          
          NSDate *bindingExpirationDate = [[NSCalendar currentCalendar] dateByAddingComponents:dayComponent toDate:lastBindingCreated options:0];
          NSDate *currentDate = [NSDate date];
          if ([bindingExpirationDate compare:currentDate] == NSOrderedDescending) {
              registrationRequired = NO;
          }
      }
      return registrationRequired;
  }
  
  - (void)credentialsInvalidated {
      NSData *cachedDeviceToken = [[NSUserDefaults standardUserDefaults] objectForKey:kCachedDeviceToken];
      if ([cachedDeviceToken length] &gt; 0) {
          [TwilioVoiceSDK unregisterWithAccessToken:kAccessToken
                                        deviceToken:cachedDeviceToken
                                         completion:^(NSError *error) {
              if (error) {
           
              } else {
                  
                 
              }
          }];
      }
  
      [[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedDeviceToken];
          
      // Remove the cached binding as credentials are invalidated
      [[NSUserDefaults standardUserDefaults] removeObjectForKey:kCachedBindingTime];
  }
  
  -(void)incomingPushReceived:(PKPushPayload *)payload withCompletionHandler:(void (^)(void))completion {
      // The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
      if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
      
      }
      
      if (completion) {
          if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion &lt; 13) {
              // Save for later when the notification is properly handled.
              self.incomingPushCompletionCallback = completion;
          } else {
             
              completion();
          }
      }
  }
  - (void)incomingPushHandled {
      if (self.incomingPushCompletionCallback) {
          self.incomingPushCompletionCallback();
          self.incomingPushCompletionCallback = nil;
      }
  }
  
  
  #pragma mark - TVONotificationDelegate
  - (void)callInviteReceived:(TVOCallInvite *)callInvite {
      
     
  
  
      
     
      [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:kCachedBindingTime];
      
      if (callInvite.callerInfo.verified != nil &amp;&amp; [callInvite.callerInfo.verified boolValue]) {
         
      }
      
      NSString *from = @&quot;Voice Bot&quot;;
      if (callInvite.from) {
          from = [callInvite.from stringByReplacingOccurrencesOfString:@&quot;client:&quot; withString:@&quot;&quot;];
      }
      
      // Always report to CallKit
      [self reportIncomingCallFrom:from withUUID:callInvite.uuid];
      self.activeCallInvites[[callInvite.uuid UUIDString]] = callInvite;
      if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion &lt; 13) {
          [self incomingPushHandled];
      }
  }
  - (void)cancelledCallInviteReceived:(TVOCancelledCallInvite *)cancelledCallInvite error:(NSError *)error {
      TVOCallInvite *callInvite;
      for (NSString *uuid in self.activeCallInvites) {
          TVOCallInvite *activeCallInvite = [self.activeCallInvites objectForKey:uuid];
          if ([cancelledCallInvite.callSid isEqualToString:activeCallInvite.callSid]) {
              callInvite = activeCallInvite;
              break;
          }
      }
      
      if (callInvite) {
          [self performEndCallActionWithUUID:callInvite.uuid];
          [self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
      }
  }
  
  
  - (void)callDidStartRinging:(TVOCall *)call {
      NSLog(@&quot;callDidStartRinging:&quot;);
    
      if (self.playCustomRingback) {
          [self playRingback];
      }
      
  //    [self.placeCallButton setTitle:@&quot;Ringing&quot; forState:UIControlStateNormal];
  }
  
  - (void)callDidConnect:(TVOCall *)call {
      NSLog(@&quot;callDidConnect:&quot;);
      
      if (self.playCustomRingback) {
          [self stopRingback];
      }
    [self sendEventWithName:@&quot;onSessionConnect&quot; body:@&quot;Connected&quot;];
      self.callKitCompletionCallback(YES);
      
      
      
  }
  
  
  - (void)call:(TVOCall *)call isReconnectingWithError:(NSError *)error {
      NSLog(@&quot;Call is reconnecting&quot;);
      
  }
  
  - (void)callDidReconnect:(TVOCall *)call {
      NSLog(@&quot;Call reconnected&quot;);
  
    
  }
  
  - (void)call:(TVOCall *)call didFailToConnectWithError:(NSError *)error {
      NSLog(@&quot;Call failed to connect: %@&quot;, error);
      
      self.callKitCompletionCallback(NO);
      [self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonFailed];
    [self sendEventWithName:@&quot;onSessionConnect&quot; body:@&quot;Failure&quot;];
      [self callDisconnected:call];
  }
  
  - (void)call:(TVOCall *)call didDisconnectWithError:(NSError *)error {
      if (error) {
          NSLog(@&quot;Call failed: %@&quot;, error);
      } else {
          NSLog(@&quot;Call disconnected&quot;);
      }
  
      if (!self.userInitiatedDisconnect) {
          CXCallEndedReason reason = CXCallEndedReasonRemoteEnded;
          if (error) {
              reason = CXCallEndedReasonFailed;
          }
          
          [self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:reason];
      }
    [self sendEventWithName:@&quot;onSessionConnect&quot; body:@&quot;Disconnected&quot;];
      [self callDisconnected:call];
  }
  
  - (void)callDisconnected:(TVOCall *)call {
      if ([call isEqual:self.activeCall]) {
          self.activeCall = nil;
      }
      [self.activeCalls removeObjectForKey:call.uuid.UUIDString];
      
      self.userInitiatedDisconnect = NO;
      
      if (self.playCustomRingback) {
          [self stopRingback];
      }
      
  
  }
  
  - (void)call:(TVOCall *)call
  didReceiveQualityWarnings:(NSSet&lt;NSNumber *&gt; *)currentWarnings
  previousWarnings:(NSSet&lt;NSNumber *&gt; *)previousWarnings {
      /**
       * currentWarnings: existing quality warnings that have not been cleared yet
       * previousWarnings: last set of warnings prior to receiving this callback
       *
       * Example:
       *   - currentWarnings: { A, B }
       *   - previousWarnings: { B, C }
       *   - intersection: { B }
       *
       * Newly raised warnings = currentWarnings - intersection = { A }
       * Newly cleared warnings = previousWarnings - intersection = { C }
       */
      NSMutableSet *warningIntersetction = [currentWarnings mutableCopy];
      [warningIntersetction intersectSet:previousWarnings];
      
      NSMutableSet *newWarnings = [currentWarnings mutableCopy];
      [newWarnings minusSet:warningIntersetction];
      if ([newWarnings count] &gt; 0) {
          [self qualityWarningUpdatePopup:newWarnings isCleared:NO];
      }
      
      NSMutableSet *clearedWarnings = [previousWarnings mutableCopy];
      [clearedWarnings minusSet:warningIntersetction];
      if ([clearedWarnings count] &gt; 0) {
          [self qualityWarningUpdatePopup:clearedWarnings isCleared:YES];
      }
  }
  - (void)qualityWarningUpdatePopup:(NSSet *)warnings isCleared:(BOOL)cleared {
      NSString *popupMessage = (cleared)? @&quot;Warnings cleared:&quot; : @&quot;Warnings detected:&quot;;
      for (NSNumber *warning in warnings) {
          NSString *warningName = [self warningString:[warning unsignedIntValue]];
          popupMessage = [popupMessage stringByAppendingString:[NSString stringWithFormat:@&quot; %@&quot;, warningName]];
      }
      
  
      [UIView animateWithDuration:1.0f
                       animations:^{
     
      } completion:^(BOOL finished) {
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
              [UIView animateWithDuration:1.0 animations:^{
              
              } completion:^(BOOL finished) {
              
              }];
          });
      }];
  }
  
  - (NSString *)warningString:(TVOCallQualityWarning)qualityWarning {
      switch (qualityWarning) {
          case TVOCallQualityWarningHighRtt:
              return @&quot;high-rtt&quot;;
              break;
          case TVOCallQualityWarningHighJitter:
              return @&quot;high-jitter&quot;;
              break;
          case TVOCallQualityWarningHighPacketsLostFraction:
              return @&quot;high-packets-lost-fraction&quot;;
              break;
          case TVOCallQualityWarningLowMos:
              return @&quot;low-mos&quot;;
              break;
          case TVOCallQualityWarningConstantAudioInputLevel:
              return @&quot;constant-audio-input-level&quot;;
              break;
          default:
              return @&quot;Unknown warning&quot;;
              break;
      }
  }
  -(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
     NSLog(@&quot;pushRegistry:didReceiveIncomingPushWithPayload:forType:&quot;);
     if ([type isEqualToString:PKPushTypeVoIP]) {
         
         // The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
       if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
             NSLog(@&quot;This is not a valid Twilio Voice notification.&quot;);
         }
       else
       {
         
       }
     }
  }
  
  /**
  * This delegate method is available on iOS 11 and above. Call the completion handler once the
  * notification payload is passed to the `TwilioVoice.handleNotification()` method.
  */
  - (void)pushRegistry:(PKPushRegistry *)registry
  didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
              forType:(PKPushType)type
  withCompletionHandler:(void (^)(void))completion {
     NSLog(@&quot;pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:&quot;);
  
     // Save for later when the notification is properly handled.
     self.incomingPushCompletionCallback = completion;
  
     
     if ([type isEqualToString:PKPushTypeVoIP]) {
         // The Voice SDK will use main queue to invoke `cancelledCallInviteReceived:error` when delegate queue is not passed
       if (![TwilioVoiceSDK handleNotification:payload.dictionaryPayload delegate:self delegateQueue:nil]) {
             NSLog(@&quot;This is not a valid Twilio Voice notification.&quot;);
         }
     }
     if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion &lt; 13) {
         // Save for later when the notification is properly handled.
         self.incomingPushCompletionCallback = completion;
     } else {
         /**
         * The Voice SDK processes the call notification and returns the call invite synchronously. Report the incoming call to
         * CallKit and fulfill the completion before exiting this callback method.
         */
         completion();
     }
  }
  
  #pragma mark - AVAudioSession
  - (void)toggleAudioRoute:(BOOL)toSpeaker {
      // The mode set by the Voice SDK is &quot;VoiceChat&quot; so the default audio route is the built-in receiver. Use port override to switch the route.
      self.audioDevice.block =  ^ {
          // We will execute `kDefaultAVAudioSessionConfigurationBlock` first.
          kTVODefaultAVAudioSessionConfigurationBlock();
          
          // Overwrite the audio route
          AVAudioSession *session = [AVAudioSession sharedInstance];
          NSError *error = nil;
          if (toSpeaker) {
              if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&amp;error]) {
                  NSLog(@&quot;Unable to reroute audio: %@&quot;, [error localizedDescription]);
              }
          } else {
              if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&amp;error]) {
                  NSLog(@&quot;Unable to reroute audio: %@&quot;, [error localizedDescription]);
              }
          }
      };
      self.audioDevice.block();
  }
  
  #pragma mark - CXProviderDelegate
  - (void)providerDidReset:(CXProvider *)provider {
      NSLog(@&quot;providerDidReset:&quot;);
      self.audioDevice.enabled = NO;
  }
  
  - (void)providerDidBegin:(CXProvider *)provider {
      NSLog(@&quot;providerDidBegin:&quot;);
  }
  
  - (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {
      NSLog(@&quot;provider:didActivateAudioSession:&quot;);
      self.audioDevice.enabled = YES;
  }
  
  - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession {
      NSLog(@&quot;provider:didDeactivateAudioSession:&quot;);
      self.audioDevice.enabled = NO;
  }
  
  - (void)provider:(CXProvider *)provider timedOutPerformingAction:(CXAction *)action {
      NSLog(@&quot;provider:timedOutPerformingAction:&quot;);
  }
  
  - (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
      NSLog(@&quot;provider:performStartCallAction:&quot;);
      
   
      
      [self.callKitProvider reportOutgoingCallWithUUID:action.callUUID startedConnectingAtDate:[NSDate date]];
      
      __weak typeof(self) weakSelf = self;
      [self performVoiceCallWithUUID:action.callUUID client:nil completion:^(BOOL success) {
          __strong typeof(self) strongSelf = weakSelf;
          if (success) {
              NSLog(@&quot;performVoiceCallWithUUID successful&quot;);
              [strongSelf.callKitProvider reportOutgoingCallWithUUID:action.callUUID connectedAtDate:[NSDate date]];
          } else {
              NSLog(@&quot;performVoiceCallWithUUID failed&quot;);
          }
          [action fulfill];
      }];
  }
  - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
      NSLog(@&quot;provider:performAnswerCallAction:&quot;);
      
      [self performAnswerVoiceCallWithUUID:action.callUUID completion:^(BOOL success) {
          if (success) {
              NSLog(@&quot;performAnswerVoiceCallWithUUID successful&quot;);
          } else {
              NSLog(@&quot;performAnswerVoiceCallWithUUID failed&quot;);
          }
      }];
      
      [action fulfill];
  }
  
  - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
      NSLog(@&quot;provider:performEndCallAction:&quot;);
      
      TVOCallInvite *callInvite = self.activeCallInvites[action.callUUID.UUIDString];
      TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
  
      if (callInvite) {
          [callInvite reject];
          [self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
      } else if (call) {
          [call disconnect];
      } else {
          NSLog(@&quot;Unknown UUID to perform end-call action with&quot;);
      }
  
      [action fulfill];
  }
  
  - (void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action {
      TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
      if (call) {
          [call setOnHold:action.isOnHold];
          [action fulfill];
      } else {
          [action fail];
      }
  }
  
  - (void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action {
      TVOCall *call = self.activeCalls[action.callUUID.UUIDString];
      if (call) {
          [call setMuted:action.isMuted];
          [action fulfill];
      } else {
          [action fail];
      }
  }
  
  #pragma mark - CallKit Actions
  
  
  - (void)reportIncomingCallFrom:(NSString *) from withUUID:(NSUUID *)uuid {
      CXHandle *callHandle = [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:from];
  
      CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init];
      callUpdate.remoteHandle = callHandle;
      callUpdate.supportsDTMF = YES;
      callUpdate.supportsHolding = YES;
      callUpdate.supportsGrouping = NO;
      callUpdate.supportsUngrouping = NO;
      callUpdate.hasVideo = NO;
  
      [self.callKitProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError *error) {
          if (!error) {
              
          }
          else {
              
          }
      }];
  }
  
  
  
  - (void)performVoiceCallWithUUID:(NSUUID *)uuid
                            client:(NSString *)client
                        completion:(void(^)(BOOL success))completionHandler {
      __weak typeof(self) weakSelf = self;
    
  
      TVOConnectOptions *connectOptions = [TVOConnectOptions optionsWithAccessToken:kAccessToken block:^(TVOConnectOptionsBuilder *builder) {
          __strong typeof(self) strongSelf = weakSelf;
        
      
        
        
          builder.params = @{kTwimlParamTo:phoneNumber};
        
          builder.uuid = uuid;
      }];
      TVOCall *call = [TwilioVoiceSDK connectWithOptions:connectOptions delegate:self];
      if (call) {
          self.activeCall = call;
          self.activeCalls[call.uuid.UUIDString] = call;
      }
      self.callKitCompletionCallback = completionHandler;
  }
  
  - (void)performAnswerVoiceCallWithUUID:(NSUUID *)uuid
                              completion:(void(^)(BOOL success))completionHandler {
      TVOCallInvite *callInvite = self.activeCallInvites[uuid.UUIDString];
      NSAssert(callInvite, @&quot;No CallInvite matches the UUID&quot;);
      
      TVOAcceptOptions *acceptOptions = [TVOAcceptOptions optionsWithCallInvite:callInvite block:^(TVOAcceptOptionsBuilder *builder) {
          builder.uuid = callInvite.uuid;
      }];
  
      TVOCall *call = [callInvite acceptWithOptions:acceptOptions delegate:self];
  
      if (!call) {
          completionHandler(NO);
      } else {
          self.callKitCompletionCallback = completionHandler;
          self.activeCall = call;
          self.activeCalls[call.uuid.UUIDString] = call;
      }
  
      [self.activeCallInvites removeObjectForKey:callInvite.uuid.UUIDString];
      
      if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion &lt; 13) {
          [self incomingPushHandled];
      }
  }
  
  #pragma mark - Ringtone
  
  - (void)playRingback {
      NSString *ringtonePath = [[NSBundle mainBundle] pathForResource:@&quot;ringtone&quot; ofType:@&quot;wav&quot;];
      if ([ringtonePath length] &lt;= 0) {
         
          return;
      }
      
      NSError *error;
      self.ringtonePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:ringtonePath] error:&amp;error];
      if (error != nil) {
    
      } else {
          self.ringtonePlayer.delegate = self;
          self.ringtonePlayer.numberOfLoops = -1;
          
          self.ringtonePlayer.volume = 1.0f;
          [self.ringtonePlayer play];
      }
  }
  
  - (void)stopRingback {
      if (!self.ringtonePlayer.isPlaying) {
          return;
      }
      
      [self.ringtonePlayer stop];
  }
  
  - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
      if (flag) {
          NSLog(@&quot;Audio player finished playing successfully&quot;);
      } else {
          NSLog(@&quot;Audio player finished playing with some error&quot;);
      }
  }
  
  - (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
      NSLog(@&quot;Decode error occurred: %@&quot;, error);
  }
  
  
  
  @end
  </code></pre>