blog.anthonyburns.co.uk http://blog.anthonyburns.co.uk Creating a Twitter Client for OSX in MacRuby - Part 3

Parsing the Xml returned from Twitter

The next step in this series is to parse the Xml data returned from the Twitter API into an array of status objects, then bind them to the table in our main window.

The article I referenced in the first post shows how to use the cocoa class NSXMLDocument and its friends to parse an xml document:
http://cocoawithlove.com/2008/09/cocoa-application-driven-by-http-data.html

Putting that together with the NSXMLElement docs at Apple:
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSXMLElement_Class/Reference/Reference.html

And some XPath documentation courtesy of W3Schools:
http://www.w3schools.com/XPath/xpath_syntax.asp

We're armed with enough knowledge to start parsing the Twitter Xml response.

In our app we're currently mapping an array of Friend objects (first_name, last_name) to the table in the main window, so we can just as easily map an array of TwitterStatus objects to the table with very little change in code.

First we'll create a TwitterStatus class with accessors for the various fields. Although we'll only be dealing with the users' names and statuses in this article, we'll take the opportunity to grab the users' profile image while we're here for the next article.

class TwitterStatus
	attr_accessor :text, :screen_name, :profile_image_url
end

Then using the example from the Cocoa With Love article and the Apple docs as a starting point, we can write the following method for our TwitterApi class to parse the Xml returned from Twitter into an array of our TwitterStatus objects.

def parseXml(responseData)

	if not responseData then return end
	err = nil
	
	# Create an XMLDocument object from the Xml string
	begin
		document = NSXMLDocument.alloc.initWithData responseData, options:NSXMLDocumentTidyXML, error:err
	rescue StandardError => e
		alert(e.to_s)
		return
	end

	# Create arrays of all the elements we want extracting from the Xml using XPath
	rootNode = document.rootElement
	statusTexts = rootNode.nodesForXPath "//status/text", error:err
	userScreenNames = rootNode.nodesForXPath "//status/user/screen_name", error:err
	userProfileImages = rootNode.nodesForXPath "//status/user/profile_image_url", error:err

	result = []
	statusTexts.length.times do |i|
	
		# Iterate over every status and populate a new TwitterStatus object with them
		status = TwitterStatus.new
		status.text = statusTexts[i].stringValue
		status.screen_name = userScreenNames[i].stringValue
		status.profile_image_url = userProfileImages[i].stringValue
		result << status
	
	end
	
	return result
	
end

Next a quick change to the timeline method so we parse the Xml and hand the resulting array to the callback.

def getTimeline(callback)

	getUrl('http://twitter.com/statuses/friends_timeline.xml', 
            true, lambda {|data| callback.call(self.parseXml(data)) })

end

Which we can then consume from our refresh button like so:

@twitter.getTimeline(lambda do |data|

	@friends = data
	@friendsTableView.reloadData
	
end)

The final thing to do to make the whole thing work is to tweak the tableView method to return the correct properties from the TwitterStatus objects.

def tableView(view, objectValueForTableColumn:column, row:index)
	friend = @friends[index]
	case column.identifier
		when 'screen_name'
			friend.screen_name
		when 'text'
			friend.text
	end
end

Now hitting the refresh button should fill the table with your 20 most recent updates from Twitter. If it doesn't, then you've done something wrong. Don't blame me.

Someone asked in the comments of one of the previous articles if I could post the whole code or upload to github. I'll do that after I post the next article in about a week.

Speaking of which, the next article will show how to download the users' profile images and use them in the table instead of their name.

]]>
Anthony Burns Development http://blog.anthonyburns.co.uk/Creating-a-Twitter-Client-for-OSX-in-MacRuby--Part-3 http://blog.anthonyburns.co.uk/Creating-a-Twitter-Client-for-OSX-in-MacRuby--Part-3 Wednesday, 5/6/2009 8:26:00 AM GMT
Creating a Twitter Client for OSX in MacRuby - Part 2

UPDATE: Part Three of this series is now available

Adding Basic Authentication

Adding Basic Authentication to a url request is pretty straight forward, in fact it only requires a header to be added to the request with the NSURLRequest.addValue:forHTTPHeaderField method. Simple huh? Not quite. The HTTP spec requires that the username and password be Base64 encoded, which is usually a case of requiring the base64 library in standard Ruby, but that isn't available in MacRuby, and there doesn't appear to be Cocoa object designed for the purpose.

After spending five minutes googling turned up exactly bot all, I decided to change tack and find out how to base64 encode myself.

How to Base64 Encode
http://email.about.com/cs/standards/a/base64_encoding.htm

So with a combination of the above article, a trusty Ruby book, blood, sweat, tears and a lot of trial and error I constructed:

class Base64

	ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-="

	def encode(str)
	
		dat = []
		str.each_byte { |b| dat << b }
		
		# Append 0s to the data to make it's length divisible by 3
		missingChars =  3 - (dat.length % 3)		
		missingChars.times { dat << 0 }
		
		result = ''
		sections = dat.length / 3
		sections.times do |i|
		
			a = dat[(i * 3) + 0] >> 2
			b = dat[(i * 3) + 0] << 4 & 63
			b = b | dat[(i * 3) + 1] >> 4
			c = dat[(i * 3) + 1] << 2 & 63 
			c = c | dat[(i * 3) + 2] >> 6
			d = dat[(i * 3) + 2] & 63
			
			if dat[(i * 3) + 1] == 0 then c = 64 end
			if dat[(i * 3) + 2] == 0 then d = 64 end
		
			result += ALPHA[a] + ALPHA[b] + ALPHA[c] + ALPHA[d]

		end

		return result
	end

end

There's no point me stepping through it, you should be able to dissect it easy enough with the referenced article, or just use it if you don't care how it works - it's not perfect and probably won't work in all scenarios, but it's good enough for Base64 encoding a username/password combination.

We can then use the Base64 class to encode our credentials and add the Basic Authentication header, which I've chosen to do in a separate method of the TwitterApi class called addAuthentication:

def addAuthentication(req)
	enc = Base64.new
	data = @username + ':' + @password
	data = enc.encode(data)
	data = 'Basic ' + data
	
	req.addValue data, forHTTPHeaderField:'Authorization'
end

I've also added a couple of attributes to the class that allow the username and password to be set:

attr_accessor :username, :password

And I've added a call to addAuthentication to the getUrl method as well as an authenticated parameter to the method signature, allowing you to specify if you want the request to be authenticated or not:

	def getUrl(urlString, authenticated, delegate)

		callback = HttpRequestCallback.new
		callback.delegate = delegate
		callback.buf = NSMutableData.new
		callback.response = nil

		url = NSURL.URLWithString(urlString)
		request = NSMutableURLRequest.requestWithURL(url, cachePolicy:POLICY, timeoutInterval:TIMEOUT)

		addAuthentication(req) if authenticated # <-- Add Authentication if required

		callback.conn = NSURLConnection.alloc.initWithRequest(request, delegate:callback)

	end

Finally I've added a getTimeline method to the class that calls getUrl for the authenticated user's timeline:

def getTimeline(callback)

	getUrl('http://twitter.com/statuses/friends_timeline.xml', true, lambda {|data| callback.call(data) })

end

We can consume this like so:

twitter = TwitterApi.new
twitter.username = 'xxx'
twitter.password = 'xxx'
twitter.getTimeline(lambda do |data|

	result = NSString.alloc.initWithData data, encoding:NSUTF8StringEncoding

	# display the result in an alert box
	alert = NSAlert.new
	alert.setMessageText(result)
	alert.runModal()	

end)

Which should download the Xml of your authenticated Twitter timeline and display it in a message box.

So we can now obtain our own timeline authenticated using Basic Authentication; next we'll parse the Xml into an array of status objects and bind them to the table in our main window. Part Three Available Here.

]]>
Anthony Burns Development http://blog.anthonyburns.co.uk/Creating-a-Twitter-Client-for-OSX-in-MacRuby--Part-2 http://blog.anthonyburns.co.uk/Creating-a-Twitter-Client-for-OSX-in-MacRuby--Part-2 Sunday, 4/26/2009 5:52:00 PM GMT
Creating a Twitter Client for OSX in MacRuby

UPDATE: Part Two of this series is now available

Howdy. This is a new series I intend to write on how to create a Twitter client in MacRuby using the Cocoa framework on OSX. There doesn't seem to be a lot of articles around on this subject, so I thought it would be beneficial to the world at large if I chronicled my own journey.

Warning: this is not a "Learning Ruby" guide, if you don't know how to program in Ruby, or have a good Ruby book at hand, then you ain't gonna get too far with this.

I first started by following the original Getting Started with MacRuby tutorial provided by Apple, which, as well as giving you a good introduction to MacRuby, gets you to the starting point for this series - we'll adapt the application you build following that tutorial into our Twitter client, so off you go and follow that. We'll see you back here in half an hour.

---

Welcome back. Now that you've followed the Apple tutorial, you should have an application that contains a table and a button - the next step, and the purpose of this first article, is to add the ability to download data from a url.

There are easy ways to do synchronous url requests, such as NSString.initWithContentsOfURL, but synchronous calls would freeze our UI while we wait for the call to complete, which is bad for the user, so we need to asynchronously request our urls.

A bit of googling found me an article in Objective-C that taught me everything we need to do to achieve our goal, unfortunately it was in Objective-C:
http://cocoawithlove.com/2008/09/cocoa-application-driven-by-http-data.html

Luckily, after searching on the methods used in the Objective-C article, I managed to find some MacRuby code that implemented async url requests, so saved myself a big Cocoa->Ruby headache:
http://github.com/psychs/limechat/blob/4c8a75aff8f3f7af10bf1e6049baeee29af7d82b/ruby/lib/pasternakclient.rb

The NSURLConnection.initWithRequest method requires a delegate be provided that meets certain criteria. The request method will immediately return and the actual request will be performed in the background - once the request completes, it will hand the result to the code in this delegate object.

Both the Obj-C and MacRuby examples passed 'self' in as that delegate, but that would limit our object to only being able to run one request at a time. Therefore I knocked up the following class to be the delegate we pass, which allows us to set a delegate for each request:

class HttpRequestCallback
	attr_accessor :delegate, :buf, :response, :conn
	
	def cancel
		if @conn
			@conn.cancel
			@conn = nil
		end
	end
  
	def connection(conn, didReceiveResponse:res)
		return if @conn != conn
		@response = res
	end
 
	def connectionDidFinishLoading(conn)
		if @response
			code = @response.statusCode
			if code.to_s =~ /^20[01]$/
				@delegate.call(@buf)
			else
				@delegate.call(false)
			end
		end
		@conn = nil
	end
  
	def connection(conn, didReceiveData:data)
		return if @conn != conn
		@buf.appendData(data)
	end
  
	def connection(conn, didFailWithError:err)
		if @conn == conn
			@delegate.call(false)
		end
		@conn = nil
	end
  
	def connection(conn, willSendRequest:req, redirectResponse:res)
		return nil if @conn != conn
		if res && res.statusCode == 302
			@delegate.call(req.URL.to_s)
			@conn = nil
			nil
		else
			req
		end
	end	
end

The idea being that we create one of these each time we request a url, the request then communicates with this class which in turn calls a method of our choosing when the request is complete. Think of it like a middle man: We want the request to call one of our methods when it completes, however, the NSUrlConnection class requires a bit of infrastructure in place to deal with things, so this class is the middleman who has the complicated conversation with NSUrlConnection and then simply hands us an envelope with the answer.

With the HttpRequestCallback class in place, we can now implement the following getUrl method in a TwitterApi class:

class TwitterApi
	TIMEOUT = 10
	POLICY = NSURLRequestReloadIgnoringLocalCacheData

	def getUrl(urlString, delegate)

		callback = HttpRequestCallback.new
		callback.delegate = delegate
		callback.buf = NSMutableData.new
		callback.response = nil

		url = NSURL.URLWithString(urlString)
		request = NSMutableURLRequest.requestWithURL(url, cachePolicy:POLICY, timeoutInterval:TIMEOUT)
		callback.conn = NSURLConnection.alloc.initWithRequest(request, delegate:callback)

	end
end

We call this method passing it a url string and a lambda method that will be called when the request returns. The lambda will be called with one parameter of type NSMutableData, which we can convert into a string using NSString.initWithData. We can therefore consume and test our getUrl method by replacing the code in the addFriend method with:

def addFriend(sender)
	twitter = TwitterApi.new
	twitter.getUrl('www.google.com', lambda do |data|

		result = NSString.alloc.initWithData data, encoding:NSUTF8StringEncoding

		# display the result in an alert box
		alert = NSAlert.new
		alert.setMessageText(result)
		alert.runModal()	

	end)
end

Now when you run your application and click on the Add button, you should get a message alert containing the HTML source for the Google homepage.

Next Article: We need to be able to authenticate ourselves with the Twitter API in order to get access to our own Twitter stream - so we'll cover Basic Authentication and Base64 encoding. Part Two Available Here.

Further Reading:

Additional MacRuby Tutorial - makes understanding Objective-C syntax easier:
http://www.macruby.org/trac/wiki/MacRubyTutorial

Apple Objective-C Messaging:
https://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articles/chapter_2_section_4.html

Apple reference for NSUrlReq:
http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSMutableURLRequest_Class/Reference/Reference.html

Article on using NSUrlReq in Objective-C:
http://cocoawithlove.com/2008/09/cocoa-application-driven-by-http-data.html

The Twitter API Documentation:
http://apiwiki.twitter.com

]]>
Anthony Burns Development http://blog.anthonyburns.co.uk/Creating-a-Twitter-Client-for-OSX-in-MacRuby http://blog.anthonyburns.co.uk/Creating-a-Twitter-Client-for-OSX-in-MacRuby Sunday, 4/19/2009 9:07:00 AM GMT
Quakk Fixed and Tweeter Tags Launched

Howdy Quakk fans.

It seems the nice folks at Twitter made a change to their API a couple of months ago that caused all applications built with the .NET framework to fail when posting status updates. Unfortunately I've been too busy with another Twitter related project (details below) to fix Quakk, until now. You can download and install the fixed version from the Quakk webpage.

Now that we've got that over and done with, I'd like to introduce you to Tweeter Tags. It's an idea that's been created by a team of four people who met over Twitter, and we built it in less than a month. It allows you to assign tags to your Twitter profile, so other folk with similar interests can find you, and you can find them.

If you're into photography and you want to find people on Twitter that are into it too, simply sign up, add the photography tag to your profile and then click the tag to see who else has tagged themselves with it. If you're having a problem with SQL Server and want to find some SQL bods to grill about it on Twitter, then click the SQL tag and we'll show you who to ask.

For more details check out the Tweeter Tags Blog or if I've sold the idea to you already, then get yourself straight to the Tweeter Tags website.

]]>
Anthony Burns Development http://blog.anthonyburns.co.uk/Quakk-Fixed-and-Tweeter-Tags-Launched http://blog.anthonyburns.co.uk/Quakk-Fixed-and-Tweeter-Tags-Launched Saturday, 2/7/2009 12:00:00 AM GMT
Windows VPS Hosting I've been reading a lot on the internets about VPS hosting lately, and although the bulk of it seems to be linux based, there are more and more hosts providing Windows VPS hosting. I was shelling out £9.38 a month each on two shared hosting plans, and had plans for another, so thought a Windows VPS host might be a better option.

After shopping about for prices online, I didn't find any particularly enticing deals; especially none that offered the unlimited bandwidth I had with my, then current, hosting company Fasthosts.

I figured it was worth giving Fasthosts a call to see if they had any future plans to provide VPS hosting. The guy I spoke to dismissed the idea of VPS hosting, saying they had no plans to introduce it as their dedicated servers were cheap enough, and that VPS was a combination of the bad sides of shared hosting and dedicated servers all rolled together - eh? He warned me that not all VPS hosts would provide full admin access so to be careful. I thought that was the nature of a VPS, that you had full admin access to your own virtual box. Never the less, his scare tactics made me do a bit more investigating before jumping from this particular shared hosting ship.

I decided to ask Twitter what their experiences of VPS hosting were, and @jesscoburn came back with a series of tweets answering different questions I posed, but warning me that he was slightly biased since he ran his own hosting company. He promised to send me an email with more details, and I asked him for some prices for hosting.

The email he sent me was a mammoth essay detailing the pros and potential pitfalls of VPS hosting. He's promised to turn it into a blog post, which I'll link to here when he does.

His company - Applied Innovations - offer a basic Windows VPS hosting package for $39.95 (around £26 in my language), which gives me 10GB storage and a whopping 500GB of monthly bandwidth. While this is not the unlimited I intially hoped to find, I can't see myself exhausting it in the short term.

I was up and running within a few hours of signing up, and accessing the server via Remote Desktop feels no different to the way I access our dedicated servers at work. The whole thing is nice and speedy and the sites run fast.

It's afforded me the ability to switch this blog to a separate subdomain (blog. rather than www.) and set it up in IIS as a separate website, enabling me to start using ASP.NET MVC as a base for the blog while keeping the rest of my site in a standard WebForms model. I also now have the facility to setup different websites in IIS for every little project idea I have, all running from a different subdomain of anthonyburns.co.uk or their own domain if needed; all for around £6 more than I was previously paying for two shared hosting plans.

If you're in the market for a VPS hosting solution, then I'd highly recommend Applied Innovations - the customer services was the best I've received in years, from any company, and the service itself is flawless.

]]>
Anthony Burns General http://blog.anthonyburns.co.uk/Windows-VPS-Hosting http://blog.anthonyburns.co.uk/Windows-VPS-Hosting Saturday, 11/15/2008 12:00:00 AM GMT
Simple Table Control in ASP.NET MVC

I'm digging the new ASP.NET MVC framework. Big style. Transitioning from classic ASP to ASP.NET left me a bit frustrated with the abstraction introduced. It was supposed to be better for us developers that the stateless nature of HTTP was being hidden away, that the framework would take care of all the state management, leaving us time to eat pizza and drink beer; but that just left me fat and drunk. Thankfully I can now dive back into having full control of my html and post data, among other things, by using MVC.

However, I don't want to hand craft every single piece of html every time, and there's a lot of things day to day that I just want rendering without a lot of effort - tables being one of them, so I set about building a simple but relatively powerful table control.

Requirements: I wanted the table to render an IEnumerable I might pull straight from a Linq query to a html table; including the ability to make each row a link, to highlight a selected row and the facility to add columns to the end of the table containing action links such as "Delete".

After Googling 'asp net mvc controls' I discovered a few different ways of creating and rendering controls under the MVC framework. I finally opted to write an extension method to the HtmlHelper class, made available through the Html property on each MVC View.

Since this probably won't be the last control I'll build, I created an interface each control could implement:

interface ISimpleControl {
    string Render(HtmlHelper helper)
}

I then created a small HtmlHelper extension method to render these simple controls:

public static string RenderControl(this HtmlHelper helper, ISimpleControl control)
{
    return control.Render(helper);
}

I can now render any simple control using:

<%=Html.RenderControl(new MyFirstControl());%>

Because my datasource could be any type of object returned from a Linq query (including anonymous types) I have to use reflection to discover the properties of these objects in order to render the columns of the table. I opted to do this as soon as the data source is set and convert it to a DataTable for easy traversal when it comes to rendering the html:

private static DataTable GetDataTableFromIEnumerable(IEnumerable dataSource)
{
    // Since IEnumerable doesn't expose an indexer to get the first element
    object obj;
    foreach (object o in data)
    {
        obj = o;
        break;
    }
    if (obj == null) return null;

    DataTable dataTable = new DataTable();

    // Use reflection to create DataColumns for each of the properties in the object
    Type type = obj.GetType();
    PropertyInfo[] propertyInfo = type.GetProperties();
    string[] keys = new string[propertyInfo.Length];
    for (int i = 0; i < keys.Length; i++)
    {
        DataColumn column = new DataColumn(propertyInfo[i].Name, typeof(string));
        dataTable.Columns.Add(column);
        keys[i] = propertyInfo[i].Name;
    }

    // Iterate through the IEnumerable and populate the DataTable from
    // each object using Reflection
    foreach (object row in dataSource)
    {
        DataRow dataRow = dataTable.NewRow();
        foreach (DataColumn column in dataTable.Columns)
        {
            object result = type.InvokeMember(column.ColumnName, BindingFlags.GetProperty, 
                                              null, row, null);
            if (result != null)
            {
        	dataRow[column] = result.ToString();
            }
        }
        dataTable.Rows.Add(dataRow);
    }

    return dataTable;
}

The first thing I do is to get the first element in the IEnumerable to perform the reflection on. Since IEnumerables don't expose an indexer, the only way I could think to do this is by performing a foreach loop and exiting as soon as I have a copy of the first element. I then get a list of the properties exposed by the object and create matching DataColumns for the DataTable. The second foreach loop populates a DataRow from each object and appends it to the table.

It's highly probable that I'll make other controls that iterate over IEnumerable data sources in this manner further down the road, so I opted to extract this functionality into an abstract base class called DataControl. This control exposes a public DataSource property for setting the IEnumerable, which converts it into a DataTable available to any derived classes through a protected property:

public abstract class DataControl : ISimpleControl
{
    protected DataTable DataTable { get; set; }

    public IEnumerable DataSource
    {
        set
        {
            DataTable = GetDataTableFromIEnumerable(value);
        }
    }

...

}

A derived class could then be used with an IEnumerable from your ViewData like so:

<%=Html.RenderControl(new MyDataControl{ DataSource = (IEnumerable)ViewData["MyLinqResult"] });%>

All that's left to do is to inherit from that DataControl and override the Render method to render the html:

public override string Render(HtmlHelper helper)
{
    if (DataTable == null) return "";

    // Use either the columns requested, or all table columns if not specified
    string[] keys = DisplayColumns ?? GetColumnKeys(DataTable);

    StringBuilder header = new StringBuilder();

    // Build the html for the table's header
    header.AppendLine("  <tr>");
    foreach (string key in keys)
    {
        header.AppendLine("    <th>" + key + "</th>");
    }
    header.AppendLine("  </tr>");

    bool altRow = true;
    StringBuilder data = new StringBuilder();

    // Render the relevant Html for each Row in the table
    foreach (DataRow row in DataTable.Rows)
    {
        // Add an 'Alt' Css class to alternating rows
        string rowClass = altRow ? "Alt" : "";

        data.AppendLine("  <tr class=\"" + rowClass + "\">");

        // Render the Html for each column of this row 
        foreach (string key in keys)
        {
            data.AppendLine("    <td>" + row[key] + "</td>");
        }

        data.AppendLine("  </tr>");

        // Switch whether or not this is an alternating row
        altRow = altRow ? false : true;
    }

    // Piece together all of the Html and return it
    StringBuilder html = new StringBuilder();
    html.AppendLine("<table>");
    html.Append(header);
    html.Append(data);
    html.AppendLine("</table>");

    return html.ToString();
}

I've also added a DisplayColumns property of type string[] to the Table class that enables me to specify the columns to render, as I might not want to render all of the properties. In line 6 I check for the existence of this property and if it hasn't been set I call GetColumnKeys() which is a private method I've written that returns a string[] of the DataColumn names from the DataTable.

I then render the html for the table header, and follow that up by rendering each row from the DataTable. I add an "Alt" class to each alternating row so I can use Css to colour the alternating rows differently.

I can now render a table in my View with a single line of code:

<%=Html.RenderControl(new Table{ DisplayColumns = new string[] { "Name", "EMail" }, 
                                 DataSource = (IEnumerable)ViewData["MyLinqResult"] });%>

I'll finish this control in the next article by adding the functionality to click a row to fire an action, highlight a selected row, and include further actions in additional columns at the end of the table.

You can download the full source code from this article here: MvcTableControl.zip

]]>
Anthony Burns Development http://blog.anthonyburns.co.uk/Simple-Table-Control-in-ASP.NET-MVC http://blog.anthonyburns.co.uk/Simple-Table-Control-in-ASP.NET-MVC Saturday, 10/18/2008 12:00:00 AM GMT
Quakk Goes Open Source

If you're just looking for the application, then you need to visit the Quakk homepage.

If you're after having a peek at the code, contributing, criticising, or even stealing some of it for use in your own Twitter application, then you're in the right place. The solution can be downloaded from CodePlex

Some ideas I have to improve Quakk going forward, that you may be interested in helping with:

  • GPS - Enable users to update their Location using GPS
  • Auto-Update Main Timeline - Enable users to set a time interval for Quakk to Update the main timline
  • Detect new software update - Quakk should check home every 25 updates to see if there's a new version
  • View X's timeline in menu - Adding "View User" to the popup menu so users can view another user's timeline
  • Unit Testing - I have yet to begin unit testing, although I've read quite a bit about it, so if someone fancies holding my hand through my first steps, that would be great!

If you're interested in participating with the development of Quakk then hit me up: @littlecharva or leave a message on CodePlex or in the comments here.

]]>
Anthony Burns Software http://blog.anthonyburns.co.uk/Quakk-Goes-Open-Source http://blog.anthonyburns.co.uk/Quakk-Goes-Open-Source Saturday, 9/20/2008 12:00:00 AM GMT
Quakk v1.0 Final Release

Yowsers! It's finally here, the final build of Quakk v1.0.

Here's a few things that have been fixed/tweaked since the last beta:

  • No longer crashes randomly when updating
  • Send Tweet window auto-closes when your Tweet is sent
  • The height of each Tweet is now ALWAYS large enough to fit the text in
  • The local time set on your device is now taken into account when displaying Tweet times
  • The direct message timeline now works
  • The drawing of each tweet is now done using double-buffering to minimize flicker

QuakkSetup.cab

Copy the CAB file to your Windows Mobile device and run it, the CAB installer will install Quakk and create a shortcut in your Programs menu.

You might need to download and install the .NET Compact Framework 2 if you're running Windows Mobile 5 - if so, you can download that here.

I don't know how much more I'm going to add to Quakk in the future, as I have other crazy contraptions I want to build, but I am planning on open sourcing it very soon, once I've had the chance to clean up the code.

Follow me on Twitter (@littlecharva) to keep abreast of any further updates.

]]>
Anthony Burns Software http://blog.anthonyburns.co.uk/Quakk-v1.0-Final-Release http://blog.anthonyburns.co.uk/Quakk-v1.0-Final-Release Tuesday, 7/29/2008 12:00:00 AM GMT
Search for an Icon In order for Quakk to move out of the Beta stage it really needs an icon. If you fancy your skills as a designer and want your work to be on the screens of hundreds of people's mobile devices then send me your best shot. Twitter me @littlecharva.

]]>
Anthony Burns Software http://blog.anthonyburns.co.uk/Search-for-an-Icon http://blog.anthonyburns.co.uk/Search-for-an-Icon Wednesday, 7/16/2008 12:00:00 AM GMT
Twitter Reply EMailer

My addiction to Twitter has grown exponentially over the last month or so. I've gone from "I just don't get it" to developing a Windows Mobile application - Quakk - so I can check my Tweets on the go.

Twitter does me the favour of sending an email when when I receive a direct message, but doesn't bother when I recieve a reply - I'd prefer it the other way around. After browsing the list of available applications at the Twitter Fan Wiki I tried a few online services that claimed to monitor your replies and send notification emails, but was satisfied by none of them. The first one I tried wouldn't let me sign up due to database connection problems, and the second one sent my notifications sporadically. So I decided I could easily write my own monitor using the parts I've already developed for Quakk.

I've made the entire code for this project available to download at the bottom of this article, but if you're not a coder and you just want a copy of the application to use yourself, then that is available for download below too.

There are four classes from Quakk I'll be using: Configuration, TwitterApi, ReplyTimeline and Tweet. I've stripped out any code that's irrelevant to this project and detailed the public interfaces of these classes below, with the main code for the project below that. Please keep in mind that Quakk is still in the beta stage, so some of this code is a bit scruffy.

Configuration

public class Configuration
{
    public string LastID
    public string Username
    public string Password
    public string EmailAddress
    public string SmtpServer
    public static Configuration LoadConfiguration()
    public void Save()
}

The Configuration class deals with loading and saving your Twitter login credentials, email details and the ID of the last received tweet to an xml file. The static method LoadConfiguration() loads and parses the Xml file and returns an instance of the Configuration class populated with the details from the Xml file. These are made available as public properties, and a Save() method enables you to save any changes you make back to the Xml file.

TwitterApi

public class TwitterApi
{
    public string Username
    public string Password
    public string GetXml(string url)
}

The TwitterApi class takes care of connecting to Twitter, authenticating your user details and returning the response as a string. Create an instance of the TwitterApi, populate the Username and Password properties from your Configuration object and then call GetXml() on the desired url from the Twitter API.

Tweet

public class Tweet
{
    public string ID
    public string DisplayName
    public DateTime TweetDateTime
    public string Message
    public Tweet(XmlNode tweetXml)
}

The Tweet class takes an XmlNode as part of its constuctor which it parses and populates the public Properties from the data provided.

ReplyTimeline

public class ReplyTimeline
{
    public List<Tweet> Tweets
    protected ReplyTimeline(TwitterApi twitterApi)
    public void Update()
}

You pass in an instance of the TwitterApi to the ReplyTimeline class when you instantiate it which gives the ReplyTimeline the ability to connect to Twitter. When you call the Update method, it obtains the Xml for the 20 most recent replies from your Twitter account, parses it and creates a List of parsed Tweet objects which it makes available from its public Tweets property.

Putting it all Together

As you can see, by making use of these objects you can get a collection of Tweets live from Twitter in very little code.

// Load Configuration from Xml file, quitting if it doesn't exist
Configuration config = Configuration.LoadConfiguration();
if(config == null) return;

TwitterApi twitter = new TwitterApi
                         {
                             Username = config.Username,
                             Password = config.Password
                         };

// Update the Reply Timeline
ReplyTimeline timeline;
try
{
    timeline = new Timeline(twitter);
    timeline.Update();
}
catch(TwitterException)
{
    // We could implement some sort of logging here,
    // but since Twitter is down more than it's up 
    // I've opted to just quit the application here
    return;
}

List<Tweet> tweets = timeline.Tweets;

At this point we have a generic List of Tweet objects from our Reply Timeline.

string emailBody = "";

// Iterate through each tweet returned from the timeline 
// until we hit a tweet we've already seen
foreach(Tweet tweet in tweets)
{
    if(tweet.ID == config.LastID)
    {
        break;
    }

    // Append the current tweet's details to our email body
    emailBody += tweet.DisplayName + " replied:\r\n" +
                 tweet.Message + "\r\n\r\nAt: " +
                 tweet.TweetDateTime + "\r\n\r\n";
}

We iterate through the Tweet list building up an email containing the tweets we haven't seen yet.

// If we have tweet notifications to send then compose the email
if(emailBody != "")
{

    MailMessage mail = new MailMessage(config.EmailAddress,
                                       config.EmailAddress,
                                       "New Reply Tweet", 
                                       emailBody);

    // Send the email using the SMTP server specified in the config
    try
    {
        SmtpClient client = new SmtpClient(config.SmtpServer);
        client.Send(mail);
    }
    catch(SmtpException)
    {
        return;
    }

    // Update the ID of the last tweet in the config so we
    // can identify if we've already seen it next time
    config.LastID = tweets[0].ID;
    config.Save();
}

Then we send out the email using the details from the config file, and update the config file with the ID of the last tweet.

You also need the following Configuration.xml file in the same directory as the application.

<?xml version="1.0"?>
<Configuration>
  <LastID></LastID>
  <Username>Your Twitter Username</Username>
  <Password>Your Twitter Password</Password>
  <EmailAddress>billy@billy.com</EmailAddress>
  <SmtpServer>smtp.mysmtpserver.com</SmtpServer>
</Configuration>

How to use the Software

You can download the source in a VS2008 project, or if you're only interested in the application itself then download the app.

The application simply connects to Twitter, downloads your Replies, compiles an email of any new Replies and sends that email to you. In order to make use of it you should set it up as a scheduled task in Windows.

]]>
Anthony Burns Software http://blog.anthonyburns.co.uk/Twitter-Reply-EMailer http://blog.anthonyburns.co.uk/Twitter-Reply-EMailer Thursday, 7/10/2008 12:00:00 AM GMT