Twilio incoming call is not working in iOS

0 votes

I am working on Twilio programmable voice SDK. I integrated it with the help of documentation and GitHub SDK. I can make a call from iOS to any number. It works fine.

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.

here is the code:

@import PushKit;
@import CallKit;
@import TwilioVoice;
@import UserNotifications;
static NSString  * kAccessToken = @"";
 NSString * phoneNumber = @"";
NSString * newToken = @"";
static NSString *const kTwimlParamTo = @"to";

static NSInteger const kRegistrationTTLInDays = 365;

NSString * const kCachedDeviceToken = @"CachedDeviceToken";
NSString * const kCachedBindingTime = @"CachedBindingTime";


@interface RCTCallPackageModule () <TVONotificationDelegate, TVOCallDelegate, CXProviderDelegate, UITextFieldDelegate, AVAudioPlayerDelegate , PushKitEventDelegate, PKPushRegistryDelegate>
@property (nonatomic, weak) id<PushKitEventDelegate> 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 <React/RCTLog.h>

@implementation RCTCallPackageModule



-(NSString *)fetchAccessToken {
 NSString *accessToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://54.172.240.200:4000/accessToken"]
                          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:@"Voice Quickstart"];
  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 = @"Voice Bot";
      
      [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(@"EndCallAction transaction request failed: %@", [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(@"StartCallAction transaction request failed: %@", [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] > 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 < 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 && [callInvite.callerInfo.verified boolValue]) {
       
    }
    
    NSString *from = @"Voice Bot";
    if (callInvite.from) {
        from = [callInvite.from stringByReplacingOccurrencesOfString:@"client:" withString:@""];
    }
    
    // Always report to CallKit
    [self reportIncomingCallFrom:from withUUID:callInvite.uuid];
    self.activeCallInvites[[callInvite.uuid UUIDString]] = callInvite;
    if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 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(@"callDidStartRinging:");
  
    if (self.playCustomRingback) {
        [self playRingback];
    }
    
//    [self.placeCallButton setTitle:@"Ringing" forState:UIControlStateNormal];
}

- (void)callDidConnect:(TVOCall *)call {
    NSLog(@"callDidConnect:");
    
    if (self.playCustomRingback) {
        [self stopRingback];
    }
  [self sendEventWithName:@"onSessionConnect" body:@"Connected"];
    self.callKitCompletionCallback(YES);
    
    
    
}


- (void)call:(TVOCall *)call isReconnectingWithError:(NSError *)error {
    NSLog(@"Call is reconnecting");
    
}

- (void)callDidReconnect:(TVOCall *)call {
    NSLog(@"Call reconnected");

  
}

- (void)call:(TVOCall *)call didFailToConnectWithError:(NSError *)error {
    NSLog(@"Call failed to connect: %@", error);
    
    self.callKitCompletionCallback(NO);
    [self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonFailed];
  [self sendEventWithName:@"onSessionConnect" body:@"Failure"];
    [self callDisconnected:call];
}

- (void)call:(TVOCall *)call didDisconnectWithError:(NSError *)error {
    if (error) {
        NSLog(@"Call failed: %@", error);
    } else {
        NSLog(@"Call disconnected");
    }

    if (!self.userInitiatedDisconnect) {
        CXCallEndedReason reason = CXCallEndedReasonRemoteEnded;
        if (error) {
            reason = CXCallEndedReasonFailed;
        }
        
        [self.callKitProvider reportCallWithUUID:call.uuid endedAtDate:[NSDate date] reason:reason];
    }
  [self sendEventWithName:@"onSessionConnect" body:@"Disconnected"];
    [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<NSNumber *> *)currentWarnings
previousWarnings:(NSSet<NSNumber *> *)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] > 0) {
        [self qualityWarningUpdatePopup:newWarnings isCleared:NO];
    }
    
    NSMutableSet *clearedWarnings = [previousWarnings mutableCopy];
    [clearedWarnings minusSet:warningIntersetction];
    if ([clearedWarnings count] > 0) {
        [self qualityWarningUpdatePopup:clearedWarnings isCleared:YES];
    }
}
- (void)qualityWarningUpdatePopup:(NSSet *)warnings isCleared:(BOOL)cleared {
    NSString *popupMessage = (cleared)? @"Warnings cleared:" : @"Warnings detected:";
    for (NSNumber *warning in warnings) {
        NSString *warningName = [self warningString:[warning unsignedIntValue]];
        popupMessage = [popupMessage stringByAppendingString:[NSString stringWithFormat:@" %@", 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 @"high-rtt";
            break;
        case TVOCallQualityWarningHighJitter:
            return @"high-jitter";
            break;
        case TVOCallQualityWarningHighPacketsLostFraction:
            return @"high-packets-lost-fraction";
            break;
        case TVOCallQualityWarningLowMos:
            return @"low-mos";
            break;
        case TVOCallQualityWarningConstantAudioInputLevel:
            return @"constant-audio-input-level";
            break;
        default:
            return @"Unknown warning";
            break;
    }
}
-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
   NSLog(@"pushRegistry:didReceiveIncomingPushWithPayload:forType:");
   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(@"This is not a valid Twilio Voice notification.");
       }
     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(@"pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:");

   // 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(@"This is not a valid Twilio Voice notification.");
       }
   }
   if ([[NSProcessInfo processInfo] operatingSystemVersion].majorVersion < 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 "VoiceChat" 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:&error]) {
                NSLog(@"Unable to reroute audio: %@", [error localizedDescription]);
            }
        } else {
            if (![session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error]) {
                NSLog(@"Unable to reroute audio: %@", [error localizedDescription]);
            }
        }
    };
    self.audioDevice.block();
}

#pragma mark - CXProviderDelegate
- (void)providerDidReset:(CXProvider *)provider {
    NSLog(@"providerDidReset:");
    self.audioDevice.enabled = NO;
}

- (void)providerDidBegin:(CXProvider *)provider {
    NSLog(@"providerDidBegin:");
}

- (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession {
    NSLog(@"provider:didActivateAudioSession:");
    self.audioDevice.enabled = YES;
}

- (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession {
    NSLog(@"provider:didDeactivateAudioSession:");
    self.audioDevice.enabled = NO;
}

- (void)provider:(CXProvider *)provider timedOutPerformingAction:(CXAction *)action {
    NSLog(@"provider:timedOutPerformingAction:");
}

- (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action {
    NSLog(@"provider:performStartCallAction:");
    
 
    
    [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(@"performVoiceCallWithUUID successful");
            [strongSelf.callKitProvider reportOutgoingCallWithUUID:action.callUUID connectedAtDate:[NSDate date]];
        } else {
            NSLog(@"performVoiceCallWithUUID failed");
        }
        [action fulfill];
    }];
}
- (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action {
    NSLog(@"provider:performAnswerCallAction:");
    
    [self performAnswerVoiceCallWithUUID:action.callUUID completion:^(BOOL success) {
        if (success) {
            NSLog(@"performAnswerVoiceCallWithUUID successful");
        } else {
            NSLog(@"performAnswerVoiceCallWithUUID failed");
        }
    }];
    
    [action fulfill];
}

- (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action {
    NSLog(@"provider:performEndCallAction:");
    
    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(@"Unknown UUID to perform end-call action with");
    }

    [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, @"No CallInvite matches the UUID");
    
    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 < 13) {
        [self incomingPushHandled];
    }
}

#pragma mark - Ringtone

- (void)playRingback {
    NSString *ringtonePath = [[NSBundle mainBundle] pathForResource:@"ringtone" ofType:@"wav"];
    if ([ringtonePath length] <= 0) {
       
        return;
    }
    
    NSError *error;
    self.ringtonePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:ringtonePath] error:&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(@"Audio player finished playing successfully");
    } else {
        NSLog(@"Audio player finished playing with some error");
    }
}

- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
    NSLog(@"Decode error occurred: %@", error);
}



@end

Nov 17 in Mobile Development by gaurav
• 22,040 points
18 views

No answer to this question. Be the first to respond.

Your answer

Your name to display (optional):
Privacy: Your email address will only be used for sending these notifications.

Related Questions In Mobile Development

0 votes
0 answers

FB Login not working with React Native iOS v13+

I've a project with following configuration. "react": "16.8.3", "react-native": ...READ MORE

Nov 10 in Mobile Development by gaurav
• 22,040 points
36 views
0 votes
0 answers

is there any working twitter integration for android and ios

I have tried so many codes for ...READ MORE

Nov 11 in Mobile Development by gaurav
• 22,040 points
22 views
0 votes
1 answer

Multiple IF statements in Excel not working

When you add an IF statement to a formula ...READ MORE

answered Nov 12 in Mobile Development by narikkadan
• 37,660 points
32 views
0 votes
0 answers

What is the equivalent of apk in iOS?

What is the equivalent of apk in ...READ MORE

Nov 15 in Mobile Development by gaurav
• 22,040 points
22 views
0 votes
0 answers
0 votes
0 answers

Is there a lightweight method to check if emoji is supported in iOS?

I'm gonna implement a emoji picker supporting Emoji ...READ MORE

Nov 22 in Mobile Development by gaurav
• 22,040 points
21 views
0 votes
0 answers

Twilio caller name on receive programmable voice call in ios application

I’m using Twilio’s Programmable Voice in one ...READ MORE

Nov 22 in Mobile Development by gaurav
• 22,040 points
25 views
0 votes
1 answer

Getting country calling prefix

For a class that offers both this ...READ MORE

answered Sep 20 in Others by Aditya
• 7,660 points
78 views
0 votes
1 answer

white screen on simulator iphone Xcode

After creating the request, you must actually ...READ MORE

answered Sep 20 in Others by Aditya
• 7,660 points
700 views
0 votes
1 answer

Is there a way to to check if a picture was taken on that iPhone?

Actually, the model and manufacturer information is ...READ MORE

answered Sep 22 in IOS by Rahul
• 9,680 points
39 views
webinar REGISTER FOR FREE WEBINAR X
REGISTER NOW
webinar_success Thank you for registering Join Edureka Meetup community for 100+ Free Webinars each month JOIN MEETUP GROUP