Using SignalR in MonoTouch and Mono for Android Apps
If you haven’t checked out SignalR yet, what are you waiting for? SignalR is a .NET library that makes it really easy to build realtime applications. The server can be hosted inside a web application, or even in a simple console app. On the client side, you can access the server from the browser through JavaScript, a console application, or even from mobile apps. Naturally my first instinct was to try and get a SignalR client working that can be used from MonoTouch and Mono for Android applications.
UPDATE 4/30/2013: Please see this more recent post for more up to date information: Updates On My SignalR Fork and Xamarin Support
Building the Client
I’d be lying if I said it took a ton of effort to get things working under Xamarin’s tools. In fact, it turned out to be a great case study in the power of the Xamarin platform. Since MonoTouch and Mono for Android expose a profile very similar to that of Windows Phone, I was able to use the Windows Phone client project as a starting point. By adding in some conditional compilation symbols in many of the same places Windows Phone had them, I quickly had a set of projects that compiled for MonoTouch and Mono for Android.
The one tricky part with iOS is that features of .NET that rely on JIT compilations will not work on a device, despite the fact that they will work in the simulator. Because of this, I added a handful of workarounds to the MonoTouch build to help work around these limitations. This included some usages of ConcurrentDictionary and some Json.NET features that relied on JIT. This was not necessary for the Android build, since Android allows for JIT compilation.
Besides the JIT workarounds, that’s really all it took to get the clients working. Think about that for a second: that means I was able to reuse all of the existing SignalR client code, and leverage it on entirely new platforms! If that’s not a great example of the power of using C# across all platforms, I’m not sure what is.
You can find my fork of SignalR on GitHub.
Sample Apps
Of course, a client library is not useful without apps that use it, so I went ahead and added a simple example app for both MonoTouch and Mono for Android to verify that everything is working as expected. You can find both of these in the samples folder in the repository. To keep things simple, both of these samples make use of the SignalR.Hosting.Self.Samples sample that comes as part of SignalR. This is a console application that listens on port 8081 for incoming connections. Make sure you fire that app up before running the samples to make sure things work correctly. Depending on how your network is set up, you may also want to update the IP addresses used by the samples to point to the correct place.
iOS
To simplify the UI creation, this sample also makes use of the MonoTouch.Dialog library. All of the work happens in the AppDelegate class in the app:
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.Dialog;
namespace SignalR.Client.MonoTouch.Sample
{
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow window;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
var message = new EntryElement("Message", "Type your message here", "");
var sendMessage = new StyledStringElement("Send Message")
{
Alignment = UITextAlignment.Center,
BackgroundColor = UIColor.Green
};
var receivedMessages = new Section();
var root = new RootElement("")
{
new Section() { message, sendMessage },
receivedMessages
};
var connection = new Connection("http://localhost:8081/echo");
sendMessage.Tapped += delegate
{
if (!string.IsNullOrWhiteSpace(message.Value) && connection.State == ConnectionState.Connected)
{
connection.Send("iOS: " + message.Value);
message.Value = "";
message.ResignFirstResponder(true);
}
};
connection.Received += data =>
{
InvokeOnMainThread(() =>
receivedMessages.Add(new StringElement(data)));
};
connection.Start().ContinueWith(task =>
connection.Send("iOS: Connected"));
var viewController = new DialogViewController(root);
window.RootViewController = viewController;
window.MakeKeyAndVisible ();
return true;
}
}
}
When the app starts up it will create a connection to the server, broadcasting a message saying it has connected once the connection is open. The use of SignalR here is extremely simple. To broadcast a message to all clients, the Send() method on the connection is used. Whenever a message is broadcast, the Received event is fired, letting us know a new message has arrived. It’s possible to send more complicated messages, but in this case we just send and receive simple strings.
Android
For the layout, all the app needs is a text field, button, and list of messages received from the server:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/Message" />
<Button
android:id="@+id/SendMessage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Send Message" />
<ListView
android:minWidth="25px"
android:minHeight="25px"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/Messages" />
</LinearLayout>
With that in place, the DemoActivity class takes care of the rest of the work:
using System.Collections.Generic;
using Android.App;
using Android.OS;
using Android.Widget;
namespace SignalR.Client.MonoDroid.Sample
{
[Activity(Label = "SignalR.Client.MonoDroid.Sample", MainLauncher = true, Icon = "@drawable/icon")]
public class DemoActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
var messageListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, new List<string>());
var messageList = FindViewById<ListView>(Resource.Id.Messages);
messageList.Adapter = messageListAdapter;
var connection = new Connection("http://10.0.2.2:8081/echo");
connection.Received += data =>
RunOnUiThread(() => messageListAdapter.Add(data));
var sendMessage = FindViewById<Button>(Resource.Id.SendMessage);
var message = FindViewById<TextView>(Resource.Id.Message);
sendMessage.Click += delegate
{
if (!string.IsNullOrWhiteSpace(message.Text) && connection.State == ConnectionState.Connected)
{
connection.Send("Android: " + message.Text);
RunOnUiThread(() => message.Text = "");
}
};
connection.Start().ContinueWith(task => connection.Send("Android: connected"));
}
}
}
Outside of the Android-specific pieces, the code here is almost identical to that of the MonoTouch app, using the same Send() method and Received event on the connection.
Summary
My goal for my SignalR fork is to keep it up to date with any point releases from the main SignalR project. It tends to be a fast moving project so I won’t pretend to be able to keep up with the master branch all the time, but whenever there’s an actual new version I will try to keep my fork in line with that. As of right now, my fork is on SignalR version 0.6. SignalR has a ton of power and potential, so I hope some of you find it useful to be able to create clients in MonoTouch and Mono for Android apps!