Using ionic with a twilio plugin

I am working on an ionic app that receives VOIP calls via Twilio. We are using the plugin https://github.com/ORCatalin/twilio_client_phonegap, which we modified from an existing plugin for using twilio with ionic 2.

Under the hood the plugin is using Twilio’s SDK for iOS or Android (depending on the type of device). It mocks Twilio’s Javascript SDK on the javascript side, which then interacts with the native code in whatever ways it needs to.

On the ionic side we are using
"@angular/core": “2.0.0-rc.1”,
“ionic-angular”: “2.0.0-beta.9”,
“ionic-native”: “1.3.0”,
for with ionic-cli version 2.0.0-beta.35 and cordova 6.4.0

This is all working well enough when we test the app after a fresh install and initiate a few test calls, but we have run into problems when we try to use the plugin in development. Specifically, when the app runs in the background for an hour or more and a call comes in, the app will either reject the call or various elements within the app will not load properly when it wakes up.

I am definitely not a native developer, so my ability to fix the plugin is quite limited. I really want to know if anyone can think of a reason why the app would behave this way after sitting in the background for so long.

Would you post your code?

I’ve written a Twilio app in Ionic, and it’s had no problem coming back from sleeping after a day or more.

Here are the relevant parts of our calls service. The registerPsychicCall function hits our API and gets back the capability token and we create the device through the Twilio.Device.Setup call.

The remainder of the code can be found in the plugin that I linked.

verifyCall(setupTwilioWithoutCallVerification?) {
    if (this.twilioWasSetup) {
      Twilio.reset();
    }

    this.twilioWasSetup = true;

    this.registerPsychicCall((error, data) => {
      if (error) {
        if (!setupTwilioWithoutCallVerification) {
          this.twilioFlowErrored();
        }
        return;
      }

      this.callClientId = data.CallClientID;
      this.callClientCapabilityToken = data.CapabilityToken;

      Twilio.Device.setup(this.callClientCapabilityToken);

      Twilio.Device.sounds.incoming(true);
      Twilio.Device.sounds.outgoing(true);

      Twilio.Device.ready(this.handleReady.bind(this));

      Twilio.Device.incoming(this.handleIncoming.bind(this));

      Twilio.Device.offline(this.handleOffline.bind(this));

      Twilio.Device.connect(this.handleConnect.bind(this));

      Twilio.Device.disconnect(this.handleDisconnect.bind(this));

    });
  }

  sendKeyPress(value) {
    this.twilioConnection.sendDigits(value);
    this.voipCallsState$.next(VOIPCallStates.VOIP);
  }

  checkVerification() {
    let verified: boolean;
    const subscription = this.store.take(1)
      .map(s => {
        return s.dashboardStore;
      })
      .let(getOldStatusId())
      .subscribe(statusId => {
        verified = [StatusNumbers.Available, StatusNumbers.VIPOK].indexOf(statusId) > -1;
      });
    subscription.unsubscribe();
    return verified;
  }

  checkForQueuedStatus() {
    let hasQueue: boolean;
    const subscription = this.store.take(1)
      .map(s => s.dashboardStore)
      .let(checkForQueuedStatus())
      .subscribe((queuedStatus: boolean) => {
        hasQueue = queuedStatus;
      });
    subscription.unsubscribe();
    return hasQueue;
  }

  handleReject() {
    if (this.checkVerification()) {
      this.handleRevertStatus();
      return;
    }
  }

  handleReady(conn) {

    if (this.checkVerification()) {
      return;
    }
    this.deviceTestCall();
  }

  handleOffline() {
    if (this.checkVerification()) {
      this.handleRevertStatus();
      this.voipCallsState$.next(VOIPCallStates.StatusCode);
      return;
    }
  }

  handleIncoming(conn: ConnectionI) {
    if (this.platform.is('android')) {
      try {
        Twilio.Device.screenIsLocked((locked) => {
          if (locked === 'true') {
            cordova.plugins.notification.local.schedule({
              title: 'Glenda',
              text: 'Incoming call',
              ongoing: true,
              at: moment().subtract(1, 'hours').toDate(),
              icon: 'res://ic_icon'
            });
          }
        });
      }

      catch (err) {
        console.log(err);
      }
    }

    if (this.checkVerification()) {
      this._tabs.dismissCurrentModal$.next(() => {
        this.manuallyChangeStatus(StatusNumbers.CallPending, moment().format('YYYY:MM:DD:HH:mm:ss'));
        this.getCallInfo();
        this.twilioConnection = conn;
        this.voipCallsState$.next(VOIPCallStates.IncomingCall);
        this._tabs.addModal({
          name: 'VOIPCallModal'
        });
      });
      return;
    }

    this.twilioConnection = conn;
    this.callVerification$.next(CallVerificationStates.VerificationCall);
  }

  getCallInfo(tries = 0) {
    this.fetchActiveCall((err, data) => {
      if (
        (
          err ||
          typeof data.FirstName !== 'string' ||
          data.FirstName.length === 0
        ) &&
        tries < 10 &&
        this.twilioConnection
      ) {
        const status = this.twilioConnection.status();
        if (['pending', 'connecting', 'open'].indexOf(status) > -1) {
          this.getCallInfo(tries + 1);
        }
      }
      ;
    });
  }

  handleAccept() {
    if (this.checkVerification()) {
      this.voipCallsState$.next(VOIPCallStates.Connecting);
      return;
    }
  }

  handleConnect() {
    if (this.checkVerification()) {
      this.manuallyChangeStatus(StatusNumbers.OnCall, moment().format('YYYY:MM:DD:HH:mm:ss'));
      this.voipCallsState$.next(VOIPCallStates.KeyPress);
      return;
    }

    this.callVerification$.next(CallVerificationStates.CheckDigit);
  }

  handleLostConnectivity() {
    if (
      this.twilioConnection &&
      typeof this.twilioConnection.status === 'function'
    ) {
      this.twilioConnection.status(status => {
        if (status === 'pending') {
          this.twilioConnection.reject();
        } else if (['open', 'connecting']) {
          this.twilioConnection.disconnect();
        }
      })
    }
    if (this.checkVerification()) {
      this.voipCallsState$.next(VOIPCallStates.StatusCode);
      return;
    }

    this.twilioFlowErrored();
  }

  handleDisconnect(conn?) {
    if (this.checkVerification()) {
      this.handleRevertStatus();
      this.fetchCalls();
      this.voipCallsState$.next(VOIPCallStates.CheckNumberOfReadings);
      return;
    }

    if (!conn) {
      return this.twilioFlowErrored();
    }
    conn.status(status => {

      conn.parameters((parameters) => {
        this.deviceTestCompleted(parameters.CallSid, status, (error, data) => {

          if (error) {
            Twilio.reset();
            this.twilioFlowErrored();
            return;
          }

          if (data.Result.ErrorCode === 0) {
            this.reconnectionsNo = 0;
            this.callVerification$.next(CallVerificationStates.PhoneVerificationSuccess);
          }
        });
      });
    });
  }
}

There’s at least a half a dozen areas to target for the bug in that code.

Beyond that, there’s really no need to insert observables where you have or to use conditional parameters. There are simple, definite calls to make. Start reading the Twilio guides - specifically the Node.JS guides. They’ll show you concise examples of everything you need to do.

5 posts were split to a new topic: I’m trying to import the Twilio plugin into an ionic 2 project but I did not get it

@ryanlogsdon, I saw that you have successfully had twilio working with ionic! Would you mind to share any sample code of how it works? As I have searched through the internet but couldn’t find examples of how to do it… I am using ionic2 , I don’t even know the way I imported the modules is right or not… I’m using the phone gap plugin by jefflinwood

1 Like

Hi @hotelger,

Here’s the trick: to use Twilio, you need an intermediary. You don’t embed Twilio’s libraries into your Ionic project directly. You use Twilio’s Node.JS libraries on your sever, and have your Ionic app make calls back to your server to kick off whatever event you want fired. Make sense?

@ryanlogsdon, thanks a lot for your reply!
Yes, indeed I have a similar setup like you mentioned. I have a server and I just use HTTP post to connect to the server. I can successfully have simple Twilio setup so that when I issue the HTTP Post command to the server, Twilio can set up to call back my verified phone number.
However, what I am trying to do is to set up a VOIP application so that my Ionic App (App X) can register to call/receive call with another App X user. I checked the documentation and seems like I need to first register to get the Access Token, and then passing the Token to a call-back function which can then trigger the call if someone calls another person through my App (App X). I successfully built an IOS app to do the above, but I am trying to do it the Hybrid way using Ionic but I cannot do (I tried to use the Plugin by Jefflin Wood which seems to claim that I can do the above but I don’t even know how to import the module after I have installed it using “ionic cordova plugin add twilio-client-phonegap-plugin”…

This is the biggest problem I run into when I’m building a new project.

I feel your pain.

Hello, can you please help me with twilio chat or fcm chat integerstion with ionic 2, it would b helful if u share the code, thankz in advance @ryanlogsdon

Twilio’s posted it all online. If you used mine, you’d be 8 months out of date. :slight_smile:

I have the same problem, is there any way on how to import twilio-chat in ionic 2?

Also still looking for a way to do this. Using the CDN and the phonegap plugin does not work

@vicruz @ryanlogsdon @hotelger
Are you have any guidelines to use twilio plugin with ionic ?

I did a Twilio app and i needed to update the token every hour and reinitiate the plugin