Sony Arouje Blog

a programmer's log

Archive for the ‘WP7’ Category

Twitter Public Timeline reader for WP7 using Caliburn Micro

with 6 comments

This is my first app for Windows Phone 7. This application doesn’t have much functionality, it just make a call to Public Timeline rest api to get the tweets. I choose PublicTimeline as it doesn’t requires any Twitter authentication. John Papa have a blog about communicating with Twitter, I used his blog to gather the details to communicate with Twitter. As usual I used my favorite MVVM frame work Caliburn Micro here as well. To kick start with WP7 development using caliburn micro I suggest to download the template from shazaml.com. The template has the CM’s WP7 bootstrapping technique mentioned by Rob Eisenberg in his blog.

Screen shot of my app.

image

In this app I used Microsoft Reactive Extension (Rx) libraries to make asynchronous call to Twitter. I combined both the Rx and Caliburn micro’s EventAggregator to do Async call and publish the data. Rx works in publisher/subscriber model, Initially I wrote an observer by my self. After I thought why should I reinvent the wheel, as Caliburn Micro has a powerful publisher/subscriber module. So I removed my observer class with EventAggregator shipped with CM. I used Rx here to avoid all the messy code that we need to write to make an async call. Jerome Laban have a blog about Rx library and is a pretty good article to start off with Rx library.

Another functionality I wanted to add to the app was caching facility, that means the app should be able to cache older tweets. Instead of fetching from Twitter we can get it from cache, if user wants to see older tweets. I was thinking of serializing my Entities to IsolatedStorage and do all the hard work by myself. Before implementing the serialization functionality I did a google search and come accross this codeplex project called winPhone7db. It did a decent job of serializing my entities to Isolated storage. As the name states its gives a feel like we are dealing with db. It encapsulates all the hurdles of communicating with Isolated storage and serialization.

Let’s jump into the details of the app. Below code explains my MainPage view model, it’s not very complicated just like any other basic view model

public class MainPageViewModel:Screen,IHandle<TimelineMessage>
{
    readonly INavigationService navigationService;

    public MainPageViewModel(INavigationService navigationService)
    {
        this.navigationService = navigationService;
        EventAggregatorHelper.EventAggregator.Subscribe(this);
        this.Tweets = new ObservableCollection<TweetViewModel>();
    }
    public ObservableCollection<TweetViewModel> Tweets { get; private set; }
    protected override void OnViewLoaded(object view)
    {
        base.OnViewLoaded(view);
        TwitterCommunicator twitterCommunicator = new TwitterCommunicator();
        
        twitterCommunicator.GetPublicTimelines("sonyarouje");
    }

    public void LoadMore()
    {
        TwitterCommunicator twitterCommunicator = new TwitterCommunicator();
        this.CreateUI(twitterCommunicator.LoadFromCache());
    }
    private void CreateUI(List<Tweet> tweets)
    {
        foreach (Tweet tweet in tweets)
        {
            TweetViewModel tweetViewModel = new TweetViewModel(tweet);
            this.Tweets.Add(tweetViewModel);
        }

    }

    public void Handle(TimelineMessage message)
    {
        if (message.Tweets != null)
        {
            this.CreateUI(message.Tweets);
            TwitterCommunicator twitterCommunicator = new TwitterCommunicator();
            twitterCommunicator.SaveToCache(message.Tweets);
        }
    }
}

I inherited the view model from Screen so that I can make use of OnViewLoaded method. The async call to Twitter orginates from onViewLoaded method. Also you can see that this viewmodel is subscribed to TimelineMessage. This is to get the notification once the Reactive Extension completes it’s async call to Twitter using webclient.

TweetViewModel is another view model to display individual tweets. All the individual TweetViewModel get added to an observable collection called Tweets and caliburn micro internally bind the respective view to an ItemsControl in my MainPageView.

Now let’s go through the Twitter communicator

public class TwitterCommunicator
{
    private readonly string FriendTimeLine = "http://twitter.com/statuses/friends_timeline/{0}.xml?count=50";
    private readonly string PublicTimeLine = "http://api.twitter.com/1/statuses/public_timeline.xml?screen_name={0}";

    public void GetPublicTimelines(string userName)
    {
        string uriString = string.Format(PublicTimeLine, userName);
        Uri uri = new Uri(uriString);
        WebClient wc = new System.Net.WebClient();
        var o = Observable.FromEvent<DownloadStringCompletedEventArgs>(wc, "DownloadStringCompleted")
                              .ObserveOn(Scheduler.ThreadPool)
                              .Select(newString => newString.EventArgs.Result);
        
        o.ObserveOn(Scheduler.Dispatcher).Subscribe(s => EventAggregatorHelper.EventAggregator.Publish<TimelineMessage>(new TimelineMessage(LoadPublicTimeLines(s.ToString()))));
        wc.DownloadStringAsync(uri);
    }

    public List<Tweet> LoadFromCache()
    {
        Repository repository = new Repository();
        return repository.GetFromCache<Tweet>();
    }

    public void SaveToCache(List<Tweet> tweets)
    {
        Repository repository = new Repository();
        repository.Add<Tweet>(tweets);
        repository.SaveChanges();
    }

    private List<Tweet> LoadPublicTimeLines(string statuses)
    {
        try
        {
            XElement xmlElement = XElement.Parse(statuses); ;
            XNamespace ns = "";
            var twitterQuery = from msg in xmlElement.Descendants(ns + "status")
                               let sender = msg.Element(ns + "user")
                               select new Tweet
                               {
                                   TwitterId = (msg.Element(ns + "id").Value).ToLong(),
                                   CreatedAt = (msg.Element(ns + "created_at").Value).ToDateTime(),
                                   Text = msg.Element(ns + "text").Value,
                                   User = new User
                                   {
                                       UserId = (sender.Element(ns + "id").Value).ToLong(),
                                       Name = sender.Element(ns + "name").Value,
                                       ScreenName = sender.Element(ns + "screen_name").Value,
                                       Description = sender.Element(ns + "description").Value,
                                       Location = sender.Element(ns + "location").Value,
                                       ProfileImageUrl = sender.Element(ns + "profile_image_url").Value,
                                       Url = sender.Element(ns + "url").Value,
                                       Protected = (sender.Element(ns + "protected").Value).ToBool(),
                                       FollowersCount = (sender.Element(ns + "followers_count").Value).ToLong()
                                   }
                               };
            List<Tweet> statusList = twitterQuery.ToList<Tweet>();
            return statusList;
        }
        catch (Exception ex)
        {
            return null;
        }
    }

}

You need to add Microsoft.Phone.Reactive assembly to avail the functionality of Rx. Also I installed the Rx setup for Silverlight 4 and added System.Observable reference shipped with the setup.

The function GetPublicTimeLines place the async call to Twitter. As I said before I use Rx to handle the async call and notify the subscriber once it’s done. I use the WebClient class to communicate with Twitter. I choose WebClient as it is very easy to use shipped with WP7, also am not doing any complex calls to twitter that requires authentication. The below code will get executed once the async call is completed.

o.ObserveOn(Scheduler.Dispatcher).Subscribe(s => EventAggregatorHelper.EventAggregator.Publish<TimelineMessage>(new TimelineMessage(LoadPublicTimeLines(s.ToString()))));

The above code does publication of the result. Before publishing I processed the result we got from twitter and converted the xml message to Tweet entity. Conversion is done by LoadPublicTimeLine method. Once the processing is done I published the message using EventAggregator.

Now Let’s see the caching part. I created a repository class to communicate with SilverlightPhoneDatabase. Below is the class I created for it.

using System;
using System.Linq;
using System.Collections.Generic;
using SilverlightPhoneDatabase;
namespace TwitterClientAPI
{
    public class Repository
    {
        private const string DATABASENAME = "TweetsCache";
        Database _db;
        private Database TweetDataBase
        {
            get
            {
                if (_db == null)
                {
                    if (Database.DoesDatabaseExists(DATABASENAME) == false)
                    {
                        _db = Database.CreateDatabase(DATABASENAME);
                        _db.Save();
                    }
                    else
                    {
                        _db = Database.OpenDatabase(DATABASENAME);
                    }
                }

                return _db;
            }
        }

        private Table<T> GetTable<T>() where T : class
        {
            Database db = this.TweetDataBase;
            if (db.Table<T>() == null)
            {
                db.CreateTable<T>();
            }

            return db.Table<T>();
        }

        public void Add<T>(List<T> entities) where T : class
        {
            Table<T> table = this.GetTable<T>();
            foreach (T entity in entities)
            {
                table.Add(entity);
            }
        }

        public void Add<T>(T entity) where T : class
        {
            Table<T> table = this.GetTable<T>();
            table.Add(entity);
        }

        public void SaveChanges()
        {
            this.TweetDataBase.BeginSave((s)=>
                {
                    if (s.Error == null)
                    {
                        //save unsuccessful, take necessary action
                    }

                });
        }

        public List<T> GetFromCache<T>() where T:class
        {
            try
            {
                var query = (from tCache in this.TweetDataBase.Table<T>() select tCache);
                List<T> cachedData = query.ToList<T>();
                return cachedData;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }
}

 

I used the same generic repository pattern explained in one of my previous post. When I wrote this app I spend most of the time in investigating about different methods of implementing Async call and caching of tweets. Developing an app for WP7 is pretty simple I spent less than 2 hrs to finish the app. Apart from the development, I spent some time to learn Rx. Through this app I got some insight of WP7 development, Reactive Extension, SilverlightPhoneDatabase and more attached to Caliburn Micro :).

If you really want to do some decent job using Twitter API then I suggest Hammock. It’s a REST library to consume REST Services. The library also supports OAuth authentication and can be used to connect to Twitter. Sudheer has blog series explaining about authentication with Twitter using Hammock. Also Scott Gu has a very good blog about the overview of WP7 development.

To run the source you need to have WP7 Development tools. You can download the tools from here.

Download Source code.

Written by Sony Arouje

October 28, 2010 at 10:57 am

Follow

Get every new post delivered to your Inbox.

Join 166 other followers

%d bloggers like this: