# Voice Mobile SDKs: Best Practices

Twilio Programmable Voice SDKs allow you to add voice-over-IP (VoIP) calling directly into your web and native mobile applications. This page provides some known best practices to make implementation easier and reliable.

You connect to Twilio from your mobile app with the Voice Mobile SDKs (Android or iOS). To learn more about how Twilio communicates with your mobile app, [read over the Voice SDK Overview page](/docs/voice/sdks#build-a-voice-sdk-application).

## On this Page

Click on a link below to jump to a specific section.

* [Debugging](#debugging)
* [Push registration and lifecycle](#push-registration-and-lifecycle)
* [Network considerations for the Voice Mobile SDKs](#network-considerations-for-the-voice-mobile-sdks)
* [AccessTokens](#accesstokens)
* [Threading Considerations](#threading-considerations)
* [Mobile Push Credential Management](#mobile-push-credential-management)
* [Mobile Preflight Test](#mobile-preflight-test)

To get the most out of this guide, use it in conjunction with the Voice [iOS](/docs/voice/sdks/ios/get-started) or [Android](/docs/voice/sdks/android/get-started) SDK Quickstarts and documentation.

## Debugging

SDK logs will help your team or Twilio Support to identify issues. Be sure to set the log level to debug or verbose (see examples below) as a first step.

These detailed logs can be shared when you open a GitHub issue or Twilio Support ticket (just remember to redact any sensitive information first).

**Android:** Set the log level to `DEBUG`.

```java
Voice.setLogLevel(LogLevel.DEBUG)
```

![Code snippet setting log level to DEBUG in Android onCreate method.](https://docs-resources.prod.twilio.com/2382a419eb6794d311db54d3fcea9249079b213e1b97e7bb62561c3fba7f973a.png)

![voice\_android\_sdk\_debug2.](https://docs-resources.prod.twilio.com/0550a33a7e1df0db9e8d7e169608ff884d3409fb54230ba680009d385c67e767.png)

**iOS:** Set the log level to TVOLogLevelVerbose or .`verbose`

```objc
    /* Obj-C */
    [VoiceClient setLogLevel:TVOLogLevelVerbose];

```

```swift
    /* Swift */ 
    VoiceClient.logLevel = .verbose
```

![Xcode showing VoiceQuickstart project with TwilioVoiceSDK log level set to debug.](https://docs-resources.prod.twilio.com/e633fc55652e79271d91742651f1ca49103c0911478370722b38681d4ecb42b4.png)

![Code snippet setting TwilioVoiceSDK audioDevice and logLevel to debug.](https://docs-resources.prod.twilio.com/1782246e6ecf543f34a6375cde0f934670f84e5955214462641b7df2e2251525.png)

## Push registration and lifecycle

### Register for push notifications on every application

**iOS:** To ensure Apple VoIP push notification delivery, initialize the `PushKit PKPushRegistry` instance at app launch (e.g. in `AppDelegate`). This allows your app to receive notification in different states, especially when the app is relaunched from the terminated state. This also eliminates the need to check if registration exists or not.\
**Note:** This functionality is already included in the [Voice iOS SDK Quickstart](https://github.com/twilio/voice-quickstart-ios/blob/master/SwiftVoiceQuickstart/AppDelegate.swift#L24).

### Push registration Time-To-Live (TTL) is one year of idle time

It's important to unregister from push notifications using the SDK's `unregister` methods ([iOS SDK method](https://twilio.github.io/twilio-voice-ios/docs/5.0.0/Classes/TwilioVoice.html#//api/name/unregisterWithAccessToken:deviceToken:completion:), [Android SDK method](https://sdk.twilio.com/android/voice/releases/6.9.0/docs/com/twilio/voice/Voice.html#unregister\(java.lang.String,com.twilio.voice.Voice.RegistrationChannel,java.lang.String,com.twilio.voice.UnregistrationListener\)) when re-launching applications with different Voice SDK identity. Otherwise your app might receive pushes for the previously logged in user. \
**Note:** This functionality is NOT included in the Android or iOS Quickstarts.

### Push deregistration when uninstalling the application is still not fully solved due to operating system limitations

We rely on the operating system (OS) to stop providing push notifications for apps that are no longer installed. Therefore, if the app is uninstalled before deregistration, bindings can become stale and cause issues. If the app is reinstalled and identity reregistered, things will start to work again.\
**iOS:** APNS & iOS do not detect if the app has been uninstalled and will still try to deliver push notifications to the binding upon being requested by Twilio Notify, i.e. Notify has no way of knowing the app is uninstalled since there is no error.\
Since the PushKit device token will be the same on the same device and the same app (unless iOS is reinstalled or reset/restored), registering with a new identity when reinstalling the app will deactivate the old binding and activate the binding for the new identity.

### Store the device token provided by the OS in persistent memory

To avoid excessive registrations, you should save the device provided by the OS in the persistent memory. Compare the values on startup and re-register only if the tokens differ.

See an [example of this in the iOS Quickstart](https://github.com/twilio/voice-quickstart-ios/blob/master/SwiftVoiceQuickstart/ViewController.swift#L252-L280).

### React to device token changes provided by the OS events and re-register with new token

#### Android

1. React to the new device token

   ```javascript
   public void onNewToken(String token) {
           super.onNewToken(token);
           Intent intent = new Intent(Constants.ACTION_FCM_TOKEN);
           LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
       }
   ```
2. Re-register for call invites with new token ([See Android SDK Quickstart example](https://github.com/twilio/voice-quickstart-android/blob/a6d7b93fc8190352fcc679ea29da83f322d7db55/app/src/main/java/com/twilio/voice/quickstart/VoiceActivity.java#L381))

   ```javascript
   case Constants.ACTION_FCM_TOKEN:
                       registerForCallInvites();
                       break;
   ```

### iOS: Use different Twilio accounts for development and production apps to avoid getting invalid APNS token error

Developers often use the same device while testing the development and production versions of their apps. Errors can occur if the developer uses the same client identity for each of the versions of the app on the same device.\
Assuming the developer configures the Push Credential sandbox option correctly and has registered the identity in both development and production apps, there will be two active bindings existing in the Twilio Notify database. These two bindings each have an associated device token, which is development- or production-specific. Over time, the certificate and Push Credential associated with each binding may expire or become invalid and will result in errors when calling the client.\
You should use a Twilio subaccount for creating AccessTokens for development and a different Twilio subaccount (or the primary Twilio account) for creating AccessTokens for the app version you will submit to the App Store.

### iOS: Avoid using the same device during development for production app installation

When you run a different environment (development then production, or vice versa) for the first time, PushKit will return the device token from the previous environment, which may not match the sandbox configuration of the Push Credential in the AccessToken. This won't result in a registration failure, but "Invalid device token" errors will occur when Twilio sends the Push Notification delivery request to APNS.

## Network considerations for the Voice Mobile SDKs

Restrictive networks may fail unless ICE servers are provided via Connect Options or Accept Options. ICE servers can be obtained from[Twilio Network Traversal Service](/docs/stun-turn).

By default, ICE gathering is done on all interfaces except some VPN type interfaces. This makes it impossible to connect to Twilio in networks that require the use of VPN. The Boolean property to enable ICE gathering on any address ports in Connect Options should be enabled to allow the gathering of ICE candidates from all available interfaces.

```java
// Android
ConnectOptions.Builder builder = new
ConnectOptions.Builder(accessToken);

builder.enableIceGatheringOnAnyAddressPorts(true);
```

```swift
// iOS
let connectOptions = ConnectOptions(accessToken: accessToken) { builder in
        builder.params = ["foo": "bar"]
        builder.uuid = uuid
        builder.enableIceGatheringOnAnyAddressPorts = true
     }

let call = TwilioVoiceSDK.connect(options: connectOptions, delegate: self)
```

## AccessTokens

You may want to set your AccessTokens' time to live (TTL) to be several hours, with 24 hours as the maximum TTL.

AccessTokens are how Twilio tracks your Voice SDK usage (and how much your account should be billed). Keep AccessTokens secure.

Keep AccessTokens up to date. You should add logic to your application that automatically keeps AccessTokens up to date that follows the steps below:

1. Confirm the TTL that is set in the AccessToken.
2. Create a timer that will track the TTL, starting at the time your mobile app receives the AccessToken and ending about 30 seconds before the AccessToken's expiration. (e.g. timer length = TTL - 30 seconds)
3. Re-register the device with the new AccessToken using the same `register` method used at startup.

## Threading Considerations

### Android: All API calls must originate from the same thread & that thread must have a looper

On Android, all Twilio Voice API calls must originate from the same thread and that thread must contain a [Looper](https://developer.android.com/reference/android/os/Looper). Many API calls invoke callbacks to notify the API user of state changes. In order for these messages to be invoked on the same thread as the thread that the API call originated from, a [Looper](https://developer.android.com/reference/android/os/Looper) is necessary. For an example of how to make all the API calls from the same thread, please look at the [Android Voice Quickstart](/docs/voice/quickstart).

## Mobile Push Credential Management

If you need to change or update your push credentials you can do so by selecting the Push Credential in the [console](https://www.twilio.com/console/voice/sdks/credentials) and adding your new `certificate` and `private key` in the text box provided on the Push Credential page. If **your APNS VoIP certificate is expiring soon or has expired you should not delete your Push Credential**, instead you should update the Push Credential. More detail is in our IOS SDK quickstart on [Github](https://github.com/twilio/voice-quickstart-ios/blob/master/Docs/managing-push-credentials.md).

## Mobile Preflight Test

On iOS and Android SDKs, you can use the [Mobile Preflight APIs](/docs/voice/sdks/mobile-preflight-test) to test a user's call and network quality before placing a voice call. This preflight test provides a detailed report about the quality of your client's connection with Twilio.
