Twitter API in AIR Mobile App

While working on an AIR Mobile app, either in Flash or Flex, you might want to integrate Twitter API in it. In one of my projects few months back, I wanted to integrate the Twitter API within the app but wasn’t able to find a complete working solution. While searching on the internet, I came across this article on Adobe DevNet. Though any complete example was not available, it proved to be a very helpful starting point. The approach doesn’t uses any specific Twitter API library but instead uses the default oAuth authentication mechanism used by Twitter.

In this article I’ll show you a simple example to create a Twitter API integrated Flex Mobile App that can post to Twitter and also verify user’s credentials.

To start with the project you would need to have two AS3 libraries:

Next, go to https://dev.twitter.com/ and create a new application. Once there, fill in the required details and add a website URL and a callback URL. Make sure what you use as the callback URL as we will need it later in the application.

Flex AIR AS3 Mobile Twitter API

Once done, go to the newly created application and change the Application Access Level to Read and Write. This is required as we will be using our app to publish tweets. Once this is done, take a not of the Consumer Key and Consumer Secret associated with this app. We will need this in our Flex Mobile App.

Next, go to Flash Builder and create a new Flex Mobile App. You can chose any App style, I selected a View Navigator App. Add the Crypto library and oAuth library to your project.

In the first view of the application, we write code to create a connection to the Twitter API, get user’s permission to allow application to read and write to his account, store the user’s access key and access secret locally and using these, read/write to user’s twitter profile. We store the details so that we can re use them for subsequent app accesses. Each time we start the app, we first verify if the locally stored access key and secret is valid.

The code for all this is written below and accompanied by self explanatory comments.

<!--?xml version="1.0" encoding="utf-8"?-->
<!-- Place non-visual elements (e.g., services, value objects) here -->
<![CDATA[                       import mx.events.FlexEvent;                         import org.iotashan.oauth.OAuthConsumer;            import org.iotashan.oauth.OAuthRequest;             import org.iotashan.oauth.OAuthSignatureMethod_HMAC_SHA1;           import org.iotashan.oauth.OAuthToken;           import org.iotashan.utils.OAuthUtil;            import org.iotashan.utils.URLEncoding;                      // App Constants            public const TWITTER_CONSUMER_KEY:String = "XXXXXXXXXXXXX";             public const TWITTER_CONSUMER_SECRET:String = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";                        // API URLs             public const VERIFY_CREDENTIALS:String = "https://api.twitter.com/1.1/account/verify_credentials.json";             private var twitterRequestURL:String = "https://api.twitter.com/oauth/request_token";           private var twitterAuthURL:String = "https://api.twitter.com/oauth/authorize";          private var twitterTokenURL:String = "https://api.twitter.com/oauth/access_token";                      public var twitterAccessObj:Object = {};            private var requestToken:OAuthToken;            private var accessToken:OAuthToken;             private var oAuthConsumer:OAuthConsumer;            private var twitterWebView:StageWebView;            private var webViewStartLocation:int;           private var accessRequest:OAuthRequest;             private var thisProfile:Object = {};                        protected var signature:OAuthSignatureMethod_HMAC_SHA1 = new OAuthSignatureMethod_HMAC_SHA1();                      // View Creation Complete           private function onCreationComplete(event:FlexEvent):void           {               //Read details from any existing file               readTwitterAccess();                                //Check if the Access Key and Access Token already Exist                if(twitterAccessObj.accessKey && twitterAccessObj.accessSecret)                 {                   verifyAccessToken();                }               else                {                   oAuthConsumer = new OAuthConsumer(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET);                   var oauth:OAuthRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_GET, twitterRequestURL, null, oAuthConsumer);                    var request:URLRequest = new URLRequest(oauth.buildRequest(new OAuthSignatureMethod_HMAC_SHA1()));                  var loader:URLLoader = new URLLoader(request);                  loader.addEventListener(Event.COMPLETE,onLoaderComplete);               }           }                       //Loaded Twitter Access Token Request URL           private function onLoaderComplete(e:Event):void             {               requestToken = OAuthUtil.getTokenFromResponse(e.currentTarget.data);                var authRequest:URLRequest = new URLRequest('http://api.twitter.com/oauth/authorize?oauth_token=' + requestToken.key);                              // StageWebView to Authorize the App                twitterWebView = new StageWebView();                twitterWebView.viewPort = new Rectangle(0, navigator.actionBar.height, width, height);              twitterWebView.stage = this.stage;              twitterWebView.assignFocus();               twitterWebView.loadURL(authRequest.url);                twitterWebView.addEventListener(LocationChangeEvent.LOCATION_CHANGE, onLocationChange);             }                       // Web View Location Change             private function onLocationChange(e:LocationChangeEvent):void           {               var location:String = e.location;                               //Here we need to extract the oAuth Verifier URL from the redirect URL              if(location.search("rjdesignz") != -1)              {                   var oAuthVerifier:String = location.substr(location.search("oauth_verifier") + 15);                     onPin(oAuthVerifier);               }           }                       // We now have the PIN, use this            private function onPin(oAuthVerifier:String):void           {               var params:Object = new Object();               params.oauth_verifier = oAuthVerifier;                              accessRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_GET, twitterTokenURL, params, oAuthConsumer, requestToken);               var accessUrlRequest:URLRequest = new URLRequest(accessRequest.buildRequest(new OAuthSignatureMethod_HMAC_SHA1()));                 var accessLoader:URLLoader = new URLLoader(accessUrlRequest);               accessLoader.addEventListener(Event.COMPLETE, onAccessRequestComplete);             }                       // Got the required details. Dispose the web view and write details to local storage.           private function onAccessRequestComplete(e:Event):void          {               accessToken = OAuthUtil.getTokenFromResponse(e.currentTarget.data);                 twitterAccessObj.accessKey = accessToken.key;               twitterAccessObj.accessSecret = accessToken.secret;                                 twitterWebView.dispose();               postConainer.includeInLayout = true;                postConainer.visible = true;                                writeTwitterAccess();           }                       // Post the message on Twitter          private function onPostToTwitter(e:MouseEvent):void             {               busyInd.visible = true;                 var params:Object = {};                 params.status = postTxtArea.text;               var consumer:OAuthConsumer = new OAuthConsumer(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET);              var token:OAuthToken = new OAuthToken(twitterAccessObj.accessKey, twitterAccessObj.accessSecret);               var postRequest:OAuthRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_POST, "https://api.twitter.com/1/statuses/update.xml", {status:postTxtArea.text}, consumer, token);                                var urlRequest:URLRequest = new URLRequest(postRequest.buildRequest(new OAuthSignatureMethod_HMAC_SHA1()));                 urlRequest.method = URLRequestMethod.POST;                              urlRequest.url = urlRequest.url.replace("&status=" + URLEncoding.encode(params.status), "");                urlRequest.data = new URLVariables("status=" + postTxtArea.text );                              var loader:URLLoader = new URLLoader(urlRequest);               loader.addEventListener(Event.COMPLETE, onTwitterPostComplete);                 loader.addEventListener(IOErrorEvent.IO_ERROR, onTwitterIOError);               loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onTwitterHttpStatus);          }                       // Credentials already exist, verify these              public function verifyAccessToken() : void          {               busyInd.visible = true;                 trace("verify access token");               var consumer:OAuthConsumer = new OAuthConsumer(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET);              var token:OAuthToken = new OAuthToken(twitterAccessObj.accessKey, twitterAccessObj.accessSecret);               var oauthRequest:OAuthRequest = new OAuthRequest( "GET", VERIFY_CREDENTIALS, null, consumer, token );               var request:URLRequest = new URLRequest( oauthRequest.buildRequest( signature, OAuthRequest.RESULT_TYPE_URL_STRING ) );                 request.method = "GET";                                 var loader:URLLoader = new URLLoader( request );                loader.addEventListener( Event.COMPLETE, verifyAccessTokenHandler );                loader.addEventListener(IOErrorEvent.IO_ERROR, onTwitterIOError);               loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, onTwitterHttpStatus);          }                       //Successfully Posted on Twitter            private function onTwitterPostComplete(e:Event):void            {               busyInd.visible = false;                postTxtArea.text = "";              trace("Tweet Success!");            }                       // Twitter IO Error             private function onTwitterIOError(e:IOErrorEvent):void          {               busyInd.visible = false;                trace("Tweet IOError!");            }                       // Twitter HTTP Status Error            private function onTwitterHttpStatus(e:HTTPStatusEvent):void            {               trace("Tweet HttpStatus!");             }                       // Access Token Exists          protected function verifyAccessTokenHandler(event:Event):void           {               trace("Valid Access Data Exists");                              busyInd.visible = false;                postConainer.includeInLayout = true;                postConainer.visible = true;            }                       public function readTwitterAccess():void            {               var file:File = File.applicationStorageDirectory.resolvePath("tw.file");                var fileStream:FileStream = new FileStream();               if(file.exists)                 {                   fileStream.open(file, FileMode.READ);                   var obj:Object = fileStream.readObject() as Object;                     twitterAccessObj.accessKey = obj.twitterAccessKey;                  twitterAccessObj.accessSecret = obj.twitterAccessSecret;                    fileStream.close();                 }               else                {                   trace("No File Present");               }           }                       public function writeTwitterAccess():void           {               var file:File = File.applicationStorageDirectory.resolvePath("tw.file");                var fileStream:FileStream = new FileStream();               fileStream.open(file, FileMode.WRITE);              var obj:Object = {};                obj.twitterAccessKey = twitterAccessObj.accessKey;              obj.twitterAccessSecret = twitterAccessObj.accessSecret;                fileStream.writeObject(obj);                fileStream.close();             }                   ]]>

&nbsp;


&nbsp;
&nbsp;

This is just a simple example to show how you can post to Twitter using oAuth library in an AIR Mobile App.

Hope you find this a helpful one.

UPDATE: Check this newer post which uses Twitter API 1.1

 

  • Stanley

    Hi, Can I know that this code using Twitter API 1.0 or 1.1?

    because I already followed your code but failed there are few error on compile time :

    TypeError: Error #1009: Cannot access a property or method of a null object reference.

    at org.iotashan.oauth::OAuthRequest/getSignableParameters()[C:\Users\Audigy Asusn45SF\Adobe Flash Builder 4.6\TestWtitterMobile\src\org\iotashan\oauth\OAuthRequest.as:183]
    at org.iotashan.oauth::OAuthRequest/getSignableString()[C:\Users\Audigy Asusn45SF\Adobe Flash Builder 4.6\TestWtitterMobile\src\org\iotashan\oauth\OAuthRequest.as:220]
    at org.iotashan.oauth::OAuthSignatureMethod_HMAC_SHA1/signRequest()[C:\Users\Audigy Asusn45SF\Adobe Flash Builder 4.6\TestWtitterMobile\src\org\iotashan\oauth\OAuthSignatureMethod_HMAC_SHA1.as:24]
    at org.iotashan.oauth::OAuthRequest/buildRequest()[C:\Users\Audigy Asusn45SF\Adobe Flash Builder 4.6\TestWtitterMobile\src\org\iotashan\oauth\OAuthRequest.as:143]
    at views::TestWtitterMobileHomeView/onPostToTwitter()[C:\Users\Audigy Asusn45SF\Adobe Flash Builder 4.6\TestWtitterMobile\src\views\TestWtitterMobileHomeView.mxml:129]
    at views::TestWtitterMobileHomeView/button1_clickHandler()[C:\Users\Audigy Asusn45SF\Adobe Flash Builder 4.6\TestWtitterMobile\src\views\TestWtitterMobileHomeView.mxml:222]
    at views::TestWtitterMobileHomeView/___TestWtitterMobileHomeView_Button2_click()[C:\Users\Audigy Asusn45SF\Adobe Flash Builder 4.6\TestWtitterMobile\src\views\TestWtitterMobi

    Need your advise .

    Thanks

  • http://rjdesignz.com Rahul Joshi

    I am using 1.0 but it can easily be used with 1.1 as well with minor changes.

  • Waqas

    for all the classes i am getting the following error and therefore i am unable to compile my code:

    1046: Type was not found or was not a compile-time constant: OAuthConsumer.
    1046: Type was not found or was not a compile-time constant: OAuthRequest.
    1046: Type was not found or was not a compile-time constant: OAuthSignatureMethod_HMAC_SHA1.
    1046: Type was not found or was not a compile-time constant: OAuthToken.
    1046: Type was not found or was not a compile-time constant: OAuthToken.

    Can you help me in resolving this issue?

  • http://rjdesignz.com Rahul Joshi

    Please make sure you have configured the liberaries well. Also, this could be because of the change in the Twitter API. The latest version 1.1 has certain changes while the code above is for 1.0

  • Alex

    Hi Rahul,

    I know this is an old post, but I was wondering if you can update for twitter api 1.1

    I have everything set up seeming to work, but am getting a 410 error. Not sure why, I thought I’ve updated everything but I’m missing something and can’t figure it out. Any help would be appreciated!

    Thanks again for what you’ve done already though!

  • William

    Hi. Your post is great and pretty much did the trick for me. I get the idea of it, However, i have a question.

    Where do you specify the redirect url? in my case, i only specified the ‘website url’ on twitter app configuration page and when the user pushes the link ‘Go to homepage’ it just loads the website url as the string ‘location’ of your code and I cant extract any PIN out of it.

    what exactly should i change to get the right url?

    i’d really appreciate your answer, cause you certainly seem to know this stuff well…

  • William

    I only had to change some settings on the twitter app side. Your code is pretty immaculate. I would have written it in the same way and I was able to port it to pure AS3 flash code thats compatible with new twitter 1.1 api in 20 min. You’re professional.THANKS.

  • http://opdesigns.com.ng Mykeels

    There is no download link for the AS3 Crypto Library

  • http://opdesigns.com.ng Mykeels

    I was able to get the Classes needed. Nd the App Authenticated and Authorized successfully. But after loading the callback url, it fails to close the StageWebView. Even when i used a button to close it manually, When trying to tweet, it traced a Tweet IO Error. How do i go around this, please?

  • http://rjdesignz.com Rahul Joshi

    Hi Mykeels,

    Ideally it should close automatically when onAccessRequestComplete() gets called and twitterWebView.dispose() is executed within that function. If it isn’t getting closed, then either this function is not getting called or it breaks somewhere within that function.
    About the issue you mentioned about Tweet IO Error, it could be the case that Twitter has updated their API to v1.1 while the above code has been written using v1.0
    I am struggling to get out some time and update this to v1.1.

  • Bhavesh Patel

    Hi Rahul,

    When will you update this to v1.1. As i am finding many issue with above code.
    Hope you will do it soon.