Authenticating Twitter on Appcelerator with social_plus.js

Authenticating Twitter on Appcelerator with social_plus.js

If you’ve been looking for a way to use any Twitter features in your Appcelerator application, check out social_plus.js on GitHub (from Aaron Sanders – Great resource for Appcelerator, Ionic Framework and many other frameworks and just code in general).

You might notice a minor inconvenience with getting this to work on Android versions > v3.3. Actually it’s an issue with Chrome not allowing evalJS() to get the OAuth PIN silently from Twitter during the OAuth authentication procedures – You’ll notice an additional webpage is loaded with a PIN… So what are you supposed to do with this PIN? Well, I’ll show you!

Around line 430 in social_plus.js, you’ll find this bit of code:

var response;
  response = e.source.evalJS('(p = document.getElementById("oauth_pin")) && p.innerHTML;');

This works just fine in iOS and ancient Android/Chrome builds, but for those Android users who have a phone made in the last 5 years, i.e. every Android user, this is the problem. Here is how I went about fixing it:

function authorizeUICallback(e) {
	if (!Ti.Android)
	{
var response;
response = e.source.evalJS('(p = document.getElementById("oauth_pin")) && p.innerHTML;');
}

 if ((Ti.Android)&&(called > 0)){
 	 var promptView = Ti.UI.createView({
          width:'80%',
          height:'120dp',
          layout: "vertical",
          color:'black',
          backgroundColor:'gray',
          bottom:"20%"
      }),
      pinField = Ti.UI.createTextField({
          width: Ti.UI.FILL,
          height: Ti.UI.SIZE,
          hintText: 'Tap Here & Enter PIN',
          color:'black',
      }),
      pinButton = Ti.UI.createButton({
          width: Ti.UI.SIZE,
          height: Ti.UI.SIZE,
          title: "Authorize"
      });
 
  Ti.API.error('showTwitterPinPrompt');
 
  webView.setHeight("60%");
  webView.setTop(0);
 
  promptView.add(pinField);
  promptView.add(pinButton);
  view.add(promptView);
  
 
  pinButton.addEventListener('click', function() {
      if (!pinField.value) {
          alert('No PIN found');
      } else {
      	called = 0;
          pin = pinField.value;
          response = 1;
         response ? ( pin = pinField.value, destroyAuthorizeUI(), receivePinCallback()) : (loadingView && loadingView.hide(), loadingContainer && loadingContainer.hide(), webView && webView.show()), loading = !1, clearInterval(intervalID), estimates[estimateID] = (new Date).getTime() - startTime, Ti.App.Properties.setString("Social-LoadingEstimates", JSON.stringify(estimates));
      }
  });
 }
 called++;
 if (!Ti.Android)
 {
response ? ( pin = response.split("<code>")[1].split("</code>")[0], destroyAuthorizeUI(), receivePinCallback()) : (loadingView && loadingView.hide(), loadingContainer && loadingContainer.hide(), webView && webView.show()), loading = !1, clearInterval(intervalID), estimates[estimateID] = (new Date).getTime() - startTime, Ti.App.Properties.setString("Social-LoadingEstimates", JSON.stringify(estimates));
}
}

For the sake of my sanity, I just target all versions of Android. If you’re NOT dealing with Android, proceed as normal:

function authorizeUICallback(e) {
	if (!Ti.Android)
	{
var response;
response = e.source.evalJS('(p = document.getElementById("oauth_pin")) && p.innerHTML;');
}

if (!Ti.Android)
 {
response ? ( pin = response.split("<code>")[1].split("</code>")[0], destroyAuthorizeUI(), receivePinCallback()) : (loadingView && loadingView.hide(), loadingContainer && loadingContainer.hide(), webView && webView.show()), loading = !1, clearInterval(intervalID), estimates[estimateID] = (new Date).getTime() - startTime, Ti.App.Properties.setString("Social-LoadingEstimates", JSON.stringify(estimates));
}
}

This is what is called on iOS and here is the Android specific bits:

if ((Ti.Android)&&(called > 0)){
 	 var promptView = Ti.UI.createView({
          width:'80%',
          height:'120dp',
          layout: "vertical",
          color:'black',
          backgroundColor:'gray',
          bottom:"20%"
      }),
      pinField = Ti.UI.createTextField({
          width: Ti.UI.FILL,
          height: Ti.UI.SIZE,
          hintText: 'Tap Here & Enter PIN',
          color:'black',
      }),
      pinButton = Ti.UI.createButton({
          width: Ti.UI.SIZE,
          height: Ti.UI.SIZE,
          title: "Authorize"
      });
 
  Ti.API.error('showTwitterPinPrompt');
 
  webView.setHeight("60%");
  webView.setTop(0);
 
  promptView.add(pinField);
  promptView.add(pinButton);
  //promptView.hide();
  view.add(promptView);
  
 
  pinButton.addEventListener('click', function() {
      if (!pinField.value) {
          alert('No PIN found');
      } else {
      	called = 0;
          pin = pinField.value;
          response = 1;
         response ? ( pin = pinField.value, /*pin = response.split("<code>")[1].split("</code>")[0]*/ destroyAuthorizeUI(), receivePinCallback()) : (loadingView && loadingView.hide(), loadingContainer && loadingContainer.hide(), webView && webView.show()), loading = !1, clearInterval(intervalID), estimates[estimateID] = (new Date).getTime() - startTime, Ti.App.Properties.setString("Social-LoadingEstimates", JSON.stringify(estimates));
      }
  });
 }
 called++;

var called (which was declared at the top of social_plus.js, not shown here) is used to keep track of which screen you’re on during the OAuth authentication. This ensures your users don’t see the pop-up prompt for the PIN when Twitter is asking if you want to allow you application to access the users Twitter details. Now, if you’re using 2-Factor Authentication on your Twitter account, as I am, you’ll see the pop-up a screen too early, but there I haven’t worked out a better solution just yet.

This should give your users a nice pop-up prompt to enter the PIN that’s shown on the Twitter page and then the app can proceed as normal.

I hope this helps you better integrate Twitter into your Appcelerator applications. I’ve tried other solutions for Appcelerator, but social_plus.js was the easiest and most robust solution I’ve found. I plan to add multi-file upload support in the near future and also handle media uploads with the new API (the way social_plus.js handles media is depreciated, but it still works as of this post).

Be sure to check out Aarons work on GitHub and his companies website – Clearly Innovative.

Leave a Reply