Monotouch

Reachability in MonoTouch

If you’re making an app that communicates with the network, at some point you’re going to need to check if the network is even there. This is where you need to use the NetworkReachability class, but it’s not particularly user-friendly. Tony Million produced a nice wrapper class, however it’s written in Obj-C not C#. Wouldn’t it be nice if there was a version in C# MonoTouch? Well now there is!

The Code & Usage

You can download the code at github here. Usage is really simple:

private Reachability _reachability;

public override void ViewDidLoad()
{
    base.ViewDidLoad();

    _reachability = new Reachability("www.google.co.uk");
    _reachability.ReachabilityUpdated += HandleReachabilityUpdated;
}

protected virtual void HandleReachabilityUpdated(object sender, ReachabilityEventArgs e)
{
    UpdateStatusLabel(e.Status, StatusLabel);
}

protected virtual void UpdateStatusLabel(ReachabilityStatus status, UILabel label)
{
    switch (status)
    {
        case ReachabilityStatus.NotReachable:
            label.Text = "Not Reachable";
            break;

        case ReachabilityStatus.ViaWiFi:
            label.Text = "Via WiFi";
            break;

        case ReachabilityStatus.ViaWWAN:
            label.Text = "Via WWAN";
            break;

        default:
            label.Text = "Unexpected status";
            break;
    }
}

Overview

To use, you create an instance of Reachability, specifying the remote host (or IP address) you need to connect to. Alternatively there are two static method that construct special versions of Reachability: ReachabilityForInternet, ReachabilityForLocalWiFi. These return an instance of Reachability that will be populated almost immediately, for this reason you need to use the CurrentStatus property of the instance, rather than relying on the update event. The update event will fire before you even have a chance to hook it up.

The ReachabilityUpdated event is fired whenever the connectivity changes. It’s highly recommended you use this event to track the connection, with mobile devices the connectivity can change at any time.

Caveats

There’s currently a bug in MonoTouch that causes the simulator to freeze when reachability is queried. The code will work fine on a device. Versions of MonoTouch designed for iOS 6 have fixed this bug, so if you’re using an iOS 6 capable version of MonoTouch you’ll be in the clear.

MonoMac

The code is designed to work with MonoMac too, however theĀ SCNetworkReachability functions haven’t been bound yet. When they do I’ll update the code to work with MonoMac as well.

Posted by Dan in C#, Guides, MonoTouch, 1 comment

wait_fences: failed to receive reply: 10004003

This error is cryptic and can have any number of causes, but the most common are:

  • Performing animations when the view isn’t on screen (use viewDidAppear, not viewWillAppear)
  • Failing to call super when overriding a method
  • Calling something in the UI from a thread other than the main UI one
  • Calling UI methods that dramatically change the view (such as presenting / hiding main views) too quickly
  • Calling UI methods before an alert has been dismissed

Basically the error is related to the UI. The message appearing in the output may not reflect the code that actually triggered it – so to debug you need to isolate the code that’s causing it. Your first port of call should be callbacks from alerts, or other popups. For me it was the UIImagePicker, I fixed it like so:

protected void FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
    var picker = sender as UIImagePickerController;

    if (picker == null)
        return;

    picker.DismissModalViewControllerAnimated(true);
    if (picker == _picker)
	_picker = null;

    // Work with the image here

    // Bug fix for fences issue
    BeginInvokeOnMainThread(() =>
    {
        // CODE THAT INTERACTS WITH THE UI HERE
    });
}

The pertinent code is the BeginInvokeOnMainThread block, this is fixing the wait_fences issue. It looks like the callback method isn’t being executed on the UI thread. I’m not sure if this is an iOS SDK thing, or a quirk added by MonoTouch. It could also be that the picker is still animating away when I start tweaking the UI, causing the issue. For example messing about with the UI in the Clicked event of a UIAlertView can sometimes cause minor issues, moving the code to the Dismissed event fixes the issues completely.

Posted by Dan in C#, iOS, MonoTouch, 0 comments

Using UIGestureRecognizer in MonoTouch

Gesture Recognisers are a great feature added in iOS 3.2 (ages ago!), and using them in MonoTouch couldn’t be simpler. I’ll skip over a discussion on how they work, if you want more info on the recognisers themselves, be sure to check out the Apple documentation.

Selectors

UIGestureRecognizer instances use selectors to call back methods in your class. Selectors are supported in MonoTouch, you just need to do a little more work:

[Export("ViewTapSelector")]
protected void OnViewTapped(UIGestureRecognizer sender)
{
    MessageLabel.Text = "View Tapped";
}

Essentially you need to create a method, then mark it for export. This tells the MonoTouch compiler to make the method callable by ObjC code. Make sure the containing class is decorated with the Register attribute (ViewControllers are automatically registered).

First Recogniser

Setting up a UIGestureRecognizer is very easy:

var tapRecogniser = new UITapGestureRecognizer(this, new MonoTouch.ObjCRuntime.Selector("ViewTapSelector"));
View.AddGestureRecognizer(tapRecogniser);

This creates a new recogniser, with a callback and assigns it to the root view for the controller. You don’t need to worry about garbage collection since the view will retain the recogniser. You can access all of the recognisers for a view by using the GestureRecognizers property.

That’s it!

Surprisingly that’s all there is to it! You can download an example project here, or just view the code snippet below to flesh out the principles:

Sample Code

private void SetupGestureRecognisers()
{
    var tapRecogniser = new UITapGestureRecognizer(this, new MonoTouch.ObjCRuntime.Selector("ViewTapSelector"));
    View.AddGestureRecognizer(tapRecogniser);

    var doubleTapRecogniser = new UITapGestureRecognizer(this, new MonoTouch.ObjCRuntime.Selector("ViewDoubleTapSelector"));
    doubleTapRecogniser.NumberOfTapsRequired = 3;
    View.AddGestureRecognizer(doubleTapRecogniser);

    var longPressRecogniser = new UILongPressGestureRecognizer(this, new MonoTouch.ObjCRuntime.Selector("LongPressSelector"));
    View.AddGestureRecognizer(longPressRecogniser);

    var panRecogniser = new UIPanGestureRecognizer(this, new MonoTouch.ObjCRuntime.Selector("LabelPanSelector"));
    DragLabel.AddGestureRecognizer(panRecogniser);
}

[Export("ViewTapSelector")]
protected void OnViewTapped(UIGestureRecognizer sender)
{
    MessageLabel.Text = "View Tapped";
}

[Export("ViewDoubleTapSelector")]
protected void OnViewDoubleTapped(UIGestureRecognizer sender)
{
    MessageLabel.Text = "View Triple Tapped";
}

[Export("LongPressSelector")]
protected void OnLongPress(UIGestureRecognizer sender)
{
    MessageLabel.Text = "View Long Pressed";
}

[Export("LabelPanSelector")]
protected void OnLabelPan(UIGestureRecognizer sender)
{
    var panRecogniser = sender as UIPanGestureRecognizer;

    if (panRecogniser == null)
        return;

    switch (panRecogniser.State)
    {
        case UIGestureRecognizerState.Began:
            _originalPosition = DragLabel.Frame.Location;
            break;

        case UIGestureRecognizerState.Cancelled:
        case UIGestureRecognizerState.Failed:
            DragLabel.Frame = new RectangleF(_originalPosition, DragLabel.Frame.Size);
            break;

        case UIGestureRecognizerState.Changed:
            var movement = panRecogniser.TranslationInView(View);
            var newPosition = new PointF(movement.X + _originalPosition.X, movement.Y + _originalPosition.Y);
            DragLabel.Frame = new RectangleF(newPosition, DragLabel.Frame.Size);
            break;
    }
}
Posted by Dan in C#, iOS, MonoTouch, Tutorials, 4 comments