Search code examples
iosreact-nativetwiliotwilio-apiverify

iOS: Twilio Verify for React Native: Exception while calling the API


I'm using Twilio Verify for React Native and I'm getting an error (log below) when using their snippet to create a Factor:

export async function createTwilioVerifyFactor(identity) {
  const pushToken = await messaging().getToken()

  const accessTokenResponse = await getTwilioVerifyAccessToken(identity)
  const hashedIdentity = accessTokenResponse.identity
  const accessToken = accessTokenResponse.token

  try {
    const factor = await TwilioVerify.createFactor(
      new PushFactorPayload(
        // factor name
        Device.deviceName ?? 'Unknown Device',
        Constants.manifest.extra.twilioVerifyServiceSid,
        hashedIdentity,
        pushToken,
        accessToken
      )
    )
    if (!factor.sid) throw new Error('Failed to create factor')
    return factor
  } catch (e) { console.log(JSON.stringify(e, null, 2) }
}

I made sure none of the required values are null/undefined, but I'm still getting an error.

Android devices give no trouble, but iOS devices do. The try / catch block results in the following log:

{
  "nativeStackAndroid": [
    {
      "lineNumber": 95,
      "file": "FactorAPIClient.kt",
      "methodName": "invoke",
      "class": "com.twilio.verify.api.FactorAPIClient$create$2"
    },
    {
      "lineNumber": 61,
      "file": "FactorAPIClient.kt",
      "methodName": "invoke",
      "class": "com.twilio.verify.api.FactorAPIClient$create$2"
    },
    {
      "lineNumber": 68,
      "file": "NetworkAdapter.kt",
      "methodName": "execute",
      "class": "com.twilio.verify.networking.NetworkAdapter"
    },
    {
      "lineNumber": 89,
      "file": "FactorAPIClient.kt",
      "methodName": "create",
      "class": "com.twilio.verify.api.FactorAPIClient"
    },
    {
      "lineNumber": 49,
      "file": "FactorRepository.kt",
      "methodName": "create",
      "class": "com.twilio.verify.domain.factor.FactorRepository"
    },
    {
      "lineNumber": 87,
      "file": "PushFactory.kt",
      "methodName": "create",
      "class": "com.twilio.verify.domain.factor.PushFactory"
    },
    {
      "lineNumber": 55,
      "file": "FactorFacade.kt",
      "methodName": "invoke",
      "class": "com.twilio.verify.domain.factor.FactorFacade$createFactor$1"
    },
    {
      "lineNumber": 43,
      "file": "FactorFacade.kt",
      "methodName": "invoke",
      "class": "com.twilio.verify.domain.factor.FactorFacade$createFactor$1"
    },
    {
      "lineNumber": 68,
      "file": "Executor.kt",
      "methodName": "run",
      "class": "com.twilio.verify.threading.Task"
    },
    {
      "lineNumber": 1167,
      "file": "ThreadPoolExecutor.java",
      "methodName": "runWorker",
      "class": "java.util.concurrent.ThreadPoolExecutor"
    },
    {
      "lineNumber": 641,
      "file": "ThreadPoolExecutor.java",
      "methodName": "run",
      "class": "java.util.concurrent.ThreadPoolExecutor$Worker"
    },
    {
      "lineNumber": 929,
      "file": "Thread.java",
      "methodName": "run",
      "class": "java.lang.Thread"
    }
  ],
  "userInfo": null,
  "message": "{60401} Exception while calling the API",
  "code": "EUNSPECIFIED",
  "line": 5737,
  "column": 45,
  "sourceURL": "http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=my.app&modulesOnly=false&runModule=true"
}

Am I potentially doing something wrong?


Solution

  • I finally found an answer to why this code was not working on iOS. My mistake was that I was trying to use a FCM token where I should have used an APNs token. As for Android, the same implementation just worked all of a sudden so I'm updating my question's title.

    My solution

    I created a helper function to get the PNs token depending on the platform using the FCM token as fallback.

    import { Platform } from 'react-native'
    import messaging from '@react-native-firebase/messaging'
    
    export default async function getPushNotificationsToken() {
      if (Platform.OS === 'ios') {
        const token = await messaging().getAPNSToken()
        if (token) return token
      }
    
      return messaging().getToken()
    }
    

    And I replaced this line in my initial code:

    const pushToken = await messaging().getToken()
    
    const pushToken = await getPushNotificationsToken()