HomeiOS Developmentios - I'm making an attempt to fetch voip token from apple...

ios – I’m making an attempt to fetch voip token from apple in react native however can’t appear to take action, I’m constructing on xcode cloud as my mac is sort of outdated. xcode is 15


I’m making an attempt to implement VoIP push notifications in my React Native app utilizing react-native-voip-push-notification. Common push notifications (FCM & Apple notifications) are working, however the VoIP token by no means arrives.

I’ve:

Added Push Notifications entitlement within the provisioning profile and entitlements file.

Enabled VoIP in background modes in Xcode.

Up to date Data.plist:

UIBackgroundModes

    audio
    remote-notification
    voip

UIFileSharingEnabled

UILaunchStoryboardName
LaunchScreen
UIRequiredDeviceCapabilities

    arm64

UISupportedInterfaceOrientations

    UIInterfaceOrientationPortrait

UISupportedInterfaceOrientations~ipad

    UIInterfaceOrientationPortrait
    UIInterfaceOrientationLandscapeLeft
    UIInterfaceOrientationLandscapeRight

UIViewControllerBasedStatusBarAppearance

AppDelegate header (AppDelegate.h):

    #import 
    #import 
    #import 
    #import 
    
    @interface AppDelegate : RCTAppDelegate 
    @finish

AppDelegate implementation (AppDelegate.mm):

#import "AppDelegate.h"
#import 
#import 
#import 

@implementation AppDelegate

- (BOOL)software:(UIApplication *)software didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSLog(@" [NATIVE] ========================================");
  NSLog(@" [NATIVE] App Launching - didFinishLaunchingWithOptions");
  NSLog(@" [NATIVE] ========================================");
  
  self.moduleName = @"sihspatient";
  self.initialProps = @{};
  
  if ([FIRApp defaultApp] == nil) {
      [FIRApp configure];
      NSLog(@" [NATIVE] Firebase configured");
  }
  
  BOOL consequence = [super application:application didFinishLaunchingWithOptions:launchOptions];
  NSLog(@" [NATIVE] React Native bridge initialized");
  
  [self voipRegistration];
  
  return consequence;
}

- (void)voipRegistration {
  NSLog(@" [NATIVE-VOIP] ========================================");
  NSLog(@" [NATIVE-VOIP] Beginning VoIP Registration");
  NSLog(@" [NATIVE-VOIP] ========================================");
  
  PKPushRegistry *pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
  NSLog(@" [NATIVE-VOIP] PKPushRegistry occasion created");
  
  pushRegistry.delegate = self;
  NSLog(@" [NATIVE-VOIP] Delegate set to AppDelegate");
  
  pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
  NSLog(@" [NATIVE-VOIP] Desired push sorts set: PKPushTypeVoIP");
  
  NSLog(@" [NATIVE-VOIP] Ready for Apple to name didUpdatePushCredentials...");
  NSLog(@" [NATIVE-VOIP] This will likely take 2-10 seconds on first launch");
  NSLog(@" [NATIVE-VOIP] Have to be working on bodily machine (not simulator)");
  NSLog(@" [NATIVE-VOIP] Should have Push Notifications functionality enabled");
}

- (void)pushRegistry:(PKPushRegistry *)registry 
didUpdatePushCredentials:(PKPushCredentials *)credentials 
             forType:(PKPushType)kind {
  NSLog(@" [NATIVE-VOIP] ========================================");
  NSLog(@" [NATIVE-VOIP] didUpdatePushCredentials CALLED!");
  NSLog(@" [NATIVE-VOIP] ========================================");
  NSLog(@" [NATIVE-VOIP] Push Kind: %@", kind);
  NSLog(@" [NATIVE-VOIP] Credentials: %@", credentials);
  
  NSData *tokenData = credentials.token;
  NSString *tokenString = [[tokenData description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@""]];
  tokenString = [tokenString stringByReplacingOccurrencesOfString:@" " withString:@""];
  NSLog(@" [NATIVE-VOIP] Token (hex): %@", tokenString);
  NSLog(@" [NATIVE-VOIP] Token size: %lu bytes", (unsigned lengthy)tokenData.size);
  
  NSLog(@" [NATIVE-VOIP] Forwarding token to React Native bridge...");
  [RNVoipPushNotificationManager didUpdatePushCredentials:credentials forType:(NSString *)type];
  NSLog(@" [NATIVE-VOIP] Token forwarded to RNVoipPushNotificationManager");
}

- (void)pushRegistry:(PKPushRegistry *)registry 
didInvalidatePushTokenForType:(PKPushType)kind {
  NSLog(@" [NATIVE-VOIP] Push token invalidated for kind: %@", kind);
}

- (void)pushRegistry:(PKPushRegistry *)registry 
didReceiveIncomingPushWithPayload:(PKPushPayload *)payload 
             forType:(PKPushType)kind 
withCompletionHandler:(void (^)(void))completion {
  NSLog(@" [NATIVE-VOIP] Incoming VoIP push acquired");
  NSLog(@" [NATIVE-VOIP] Payload: %@", payload.dictionaryPayload);
  [RNVoipPushNotificationManager didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
  
  if (completion) {
    completion();
  }
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  return [self bundleURL];
}

- (NSURL *)bundleURL
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"principal" withExtension:@"jsbundle"];
#endif
}

@finish

VoIP service in TypeScript:

import {Platform} from 'react-native';
import VoipPushNotification from 'react-native-voip-push-notification';
import AsyncStorage from '@react-native-async-storage/async-storage';

class VoIPService {
  personal static occasion: VoIPService;

  static getInstance(): VoIPService {
    if (!VoIPService.occasion) {
      VoIPService.occasion = new VoIPService();
    }
    return VoIPService.occasion;
  }

  /**
   * Initialize VoIP - ONLY for token retrieval
   */
  async initialize(): Promise {
    if (Platform.OS !== 'ios') {
      console.log(
        '⚠️ [VOIP] VoIP solely accessible on iOS, present platform:',
        Platform.OS,
      );
      return;
    }

    console.log('🚀 [VOIP] ========================================');
    console.log('🚀 [VOIP] Beginning VoIP Token Retrieval');
    console.log('🚀 [VOIP] ========================================');

    attempt {
      // ✅ Step 1: Examine if token already exists in storage
      console.log(
        '📱 [VOIP] Step 1: Checking AsyncStorage for present token...',
      );
      const existingToken = await AsyncStorage.getItem('VOIP_TOKEN');

      if (existingToken) {
        console.log('✅ [VOIP] Discovered present token in storage!');
        console.log(
          '📱 [VOIP] Token (first 30 chars):',
          existingToken.substring(0, 30) + '...',
        );
        console.log('📱 [VOIP] Token size:', existingToken.size);
      } else {
        console.log('⚠️ [VOIP] No present token present in storage');
      }

      // ✅ Step 2: Setup listener for brand spanking new token
      console.log('📱 [VOIP] Step 2: Establishing token listener...');

      VoipPushNotification.addEventListener(
        'register',
        async (token: string) => {
          console.log('🎉 [VOIP] ========================================');
          console.log('🎉 [VOIP] TOKEN RECEIVED FROM APPLE!');
          console.log('🎉 [VOIP] ========================================');
          console.log(
            '📱 [VOIP] Token (first 30 chars):',
            token.substring(0, 30) + '...',
          );
          console.log('📱 [VOIP] Token size:', token.size);
          console.log('📱 [VOIP] Full token:', token);

          attempt {
            await AsyncStorage.setItem('VOIP_TOKEN', token);
            console.log('✅ [VOIP] Token saved to AsyncStorage efficiently!');

            // Confirm it was saved
            const savedToken = await AsyncStorage.getItem('VOIP_TOKEN');
            if (savedToken === token) {
              console.log(
                '✅ [VOIP] Token verification profitable - token matches!',
              );
            } else {
              console.log(
                '❌ [VOIP] Token verification FAILED - tokens don't match!',
              );
            }
          } catch (saveError) {
            console.error(
              '❌ [VOIP] Error saving token to AsyncStorage:',
              saveError,
            );
          }
        },
      );

      console.log('✅ [VOIP] Token listener registered');
      console.log('⏳ [VOIP] Ready for token from Apple PushKit...');
      console.log(
        '📝 [VOIP] Observe: Token comes from AppDelegate.mm native code',
      );
    } catch (error) {
      console.error('❌ [VOIP] Error throughout initialization:', error);
      console.error('❌ [VOIP] Error particulars:', JSON.stringify(error, null, 2));
    }
  }

  /**
   * Get saved VoIP token
   */
  async getVoIPToken(): Promise {
    console.log('🔍 [VOIP] Trying to retrieve token from storage...');

    attempt {
      const token = await AsyncStorage.getItem('VOIP_TOKEN');

      if (token) {
        console.log('✅ [VOIP] Token present in storage!');
        console.log(
          '📱 [VOIP] Token (first 30 chars):',
          token.substring(0, 30) + '...',
        );
        console.log('📱 [VOIP] Token size:', token.size);
        return token;
      } else {
        console.log('⚠️ [VOIP] No token present in storage');
        console.log(
          '💡 [VOIP] Token ought to be acquired from AppDelegate.mm after app launch',
        );
        return null;
      }
    } catch (error) {
      console.error('❌ [VOIP] Error retrieving token from storage:', error);
      return null;
    }
  }

  /**
   * Clear up listener
   */
  async cleanup(): Promise {
    if (Platform.OS === 'ios') {
      console.log('🧹 [VOIP] Cleansing up token listener...');
      VoipPushNotification.removeEventListener('register');
      console.log('✅ [VOIP] Cleanup full');
    }
  }
}

export default VoIPService;

VoIP Debug Display in React Native:

import React, {useEffect, useState} from 'react';
import {
  View,
  Textual content,
  StyleSheet,
  ScrollView,
  TouchableOpacity,
  Platform,
  Alert,
  Clipboard,
} from 'react-native';
import VoIPService from './providers/voip.service';
import AsyncStorage from '@react-native-async-storage/async-storage';

const VoIPDebugScreen = () => {
  const [voipToken, setVoipToken] = useState(null);
  const [loading, setLoading] = useState(true);
  const [logs, setLogs] = useState([]);

  const addLog = (message: string) => {
    const timestamp = new Date().toLocaleTimeString();
    setLogs(prev => [`[${timestamp}] ${message}`, ...prev].slice(0, 50));
  };

  useEffect(() => {
    checkVoIPToken();

    // Examine token each 2 seconds
    const interval = setInterval(checkVoIPToken, 2000);
    return () => clearInterval(interval);
  }, []);

  const checkVoIPToken = async () => {
    attempt {
      addLog('Checking VoIP token...');
      const token = await VoIPService.getInstance().getVoIPToken();
      setVoipToken(token);
      setLoading(false);

      if (token) {
        addLog(`✅ Token discovered: ${token.substring(0, 20)}...`);
      } else {
        addLog('⚠️ No token discovered but');
      }
    } catch (error) {
      addLog(`❌ Error: ${error}`);
      setLoading(false);
    }
  };

  const copyToClipboard = () => {
    if (voipToken) {
      Clipboard.setString(voipToken);
      Alert.alert('Copied!', 'VoIP token copied to clipboard');
      addLog('📋 Token copied to clipboard');
    }
  };

  const reinitializeVoIP = async () => {
    attempt {
      addLog('🔄 Reinitializing VoIP service...');
      await VoIPService.getInstance().initialize();
      addLog('✅ VoIP service reinitialized');
      setTimeout(checkVoIPToken, 1000);
    } catch (error) {
      addLog(`❌ Reinitialization error: ${error}`);
    }
  };

  const clearToken = async () => {
    attempt {
      await AsyncStorage.removeItem('VOIP_TOKEN');
      setVoipToken(null);
      addLog('🗑️ Token cleared from storage');
      Alert.alert('Success', 'VoIP token cleared');
    } catch (error) {
      addLog(`❌ Clear error: ${error}`);
    }
  };

  if (Platform.OS !== 'ios') {
    return (
      
        VoIP is simply accessible on iOS
      
    );
  }

  return (
    
      
        VoIP Debug Display
        Platform: {Platform.OS}
      

      {/* Token Standing */}
      
        Token Standing
        
          
            {loading
              ? '⏳ Loading...'
              : voipToken
              ? '✅ Token Available'
              : '❌ No Token'}
          
        
      

      {/* Token Display */}
      {voipToken && (
        
          VoIP Token
          
            
              {voipToken}
            
          
          
            📋 Copy Token
          
        
      )}

      {/* Actions */}
      
        Actions
        
          🔄 Refresh Token
        

        
          🔧 Reinitialize VoIP
        

        
          🗑️ Clear Token
        
      

      {/* Logs */}
      
        Logs (Last 50)
        
          {logs.length === 0 ? (
            No logs yet...
          ) : (
            logs.map((log, index) => (
              
                {log}
              
            ))
          )}
        
      

      {/* Instructions */}
      
        📖 Instructions
        
          1. Install this build on a physical iOS device{'n'}
          2. Launch the app{'n'}
          3. Wait 5-10 seconds for token registration{'n'}
          4. Token should appear above{'n'}
          5. Copy and save the token for backend testing{'n'}
          6. Remove this screen before production release
        
      
    
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  header: {
    backgroundColor: '#007AFF',
    padding: 20,
    paddingTop: 60,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    color: '#fff',
    marginBottom: 5,
  },
  subtitle: {
    fontSize: 14,
    color: '#fff',
    opacity: 0.8,
  },
  section: {
    backgroundColor: '#fff',
    margin: 10,
    padding: 15,
    borderRadius: 10,
    shadowColor: '#000',
    shadowOffset: {width: 0, height: 2},
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: '600',
    marginBottom: 10,
    color: '#333',
  },
  statusCard: {
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
  },
  statusText: {
    fontSize: 16,
    fontWeight: '600',
  },
  tokenCard: {
    backgroundColor: '#f8f9fa',
    padding: 15,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: '#dee2e6',
    marginBottom: 10,
  },
  tokenText: {
    fontSize: 12,
    fontFamily: 'Courier',
    color: '#495057',
  },
  button: {
    padding: 15,
    borderRadius: 8,
    alignItems: 'center',
    marginTop: 10,
  },
  primaryButton: {
    backgroundColor: '#007AFF',
  },
  secondaryButton: {
    backgroundColor: '#6c757d',
  },
  dangerButton: {
    backgroundColor: '#dc3545',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
  logsContainer: {
    backgroundColor: '#000',
    padding: 10,
    borderRadius: 8,
    maxHeight: 300,
  },
  logText: {
    color: '#0f0',
    fontSize: 11,
    fontFamily: 'Courier',
    marginBottom: 3,
  },
  noLogsText: {
    color: '#888',
    textAlign: 'center',
    fontStyle: 'italic',
  },
  instructionText: {
    fontSize: 14,
    color: '#666',
    lineHeight: 22,
  },
  errorText: {
    fontSize: 18,
    color: '#dc3545',
    textAlign: 'center',
    marginTop: 100,
  },
});

export default VoIPDebugScreen;

Problem:

I’m running this on a physical device via TestFlight.

Regular push notifications work.

VoIP push token never arrives.

Cannot check device logs because my iPhone 16 is iOS 26.1, which Xcode 15.1 does not support, and idevicesyslog fails (ERROR: Could not connect to lockdownd: -18).

I read that starting iOS 15+, the old PushKit entitlement key is removed and no longer required.

Am I missing any setup to get the VoIP token?

Is there a known issue with TestFlight builds not receiving VoIP push tokens?

Are there alternative ways to verify the VoIP token when device logs cannot be accessed?

Any guidance would be really appreciated.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments