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, 2022 in Mobile Development by gaurav
• 23,260 points
691 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, 2022 in Mobile Development by gaurav
• 23,260 points
1,231 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, 2022 in Mobile Development by gaurav
• 23,260 points
343 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, 2022 in Mobile Development by narikkadan
• 63,600 points
527 views
0 votes
0 answers

What is the equivalent of apk in iOS?

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

Nov 15, 2022 in Mobile Development by gaurav
• 23,260 points
423 views
0 votes
1 answer

Is there any library to implement twitter web view login dialog in iOS

Solution to your problem is to implement ...READ MORE

answered Dec 15, 2022 in Mobile Development by gaurav
• 23,260 points
430 views
0 votes
1 answer

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

You can easily override theme by wrapping ...READ MORE

answered Dec 12, 2022 in Mobile Development by gaurav
• 23,260 points
542 views
0 votes
1 answer

Twilio caller name on receive programmable voice call in ios application

Twilio developer evangelist here. In order to get ...READ MORE

answered Dec 15, 2022 in Mobile Development by gaurav
• 23,260 points
913 views
0 votes
1 answer

Getting country calling prefix

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

answered Sep 20, 2022 in Others by Aditya
• 7,680 points
822 views
0 votes
1 answer

white screen on simulator iphone Xcode

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

answered Sep 20, 2022 in Others by Aditya
• 7,680 points
3,865 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, 2022 in IOS by Rahul
• 9,680 points
646 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