Month: August 2012

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

Using the Keychain in MonoMac

The keychain is a great feature in OS X, allowing you to store passwords securely. In this tutorial I’ll outline how to store passwords in the keychain so your apps don’t have to worry about securely storing the passwords themselves.

Before I start, you can skip straight to the code on github here.

A few pointers

The MonoMac binding of the OS X keychain is somewhat basic at this moment in time, however it should be enough for basic password storage. OS X supports multiple keychains, as well as multiple types of passwords. At the moment the MonoMac bindings limit you to the system (well, user’s login) keychain, and only the ‘Internet Password’ keychain type.

Additionally you’ll want to sign your app to ensure your passwords are uniquely identified, without signing I’ve noticed that other MonoMac apps authored by myself seem to conflict with each other.

Relevant Classes

We store keychain records in a class of SecRecord, while the SecKeyChain static class handles the interaction with the keychain itself. When searching for records, you’ll fill in a SecRecord with the fields you need to match, then get SecKeyChain to return a matching record. You need to be careful not to try and insert duplicate records, if you do it’ll be difficult to pull out the record you want – it’s undefined which one you’ll get. This can lead to odd bugs where one second you get the correct password, and the next you get the wrong one.

The password for Internet Passwords is stored in the ValueData property, this is of NSData type rather than the string you’re probably expecting.

Selecting

To fetch a record, you need to provide a record with the fields you want to filter by set. At a minimum you’ll want to specify the service and account (username):

var searchRecord = new SecRecord(SecKind.InternetPassword)
{
    Service = ServiceName,
    Account = username
};

SecStatusCode code;
var data = SecKeyChain.QueryAsRecord(searchRecord, out code);

if (code == SecStatusCode.Success)
    return data;
else
    return null;

This code will return a matching record, or NULL if none are found. If more than one record matches your search criteria, you’ll get a random record. For this reason make sure you never accidentally insert duplicate records, or you’ll get unpredictable behaviour in your app. Remember, the search record you specify will be used as the search criteria – the filled in fields will be used for the search.

To get the password from the record use the following code:

password = NSString.FromData(record.ValueData, NSStringEncoding.UTF8);

Remember, the ValueData property is NSData, not a string. If MonoMac allows you to use a record type of ‘Generic Password’, the password will be stored in the Generic property rather than ValueData. Generic is also an NSData type.

Inserting

Inserting a password involves filling out a SecRecord, and then calling Add() on SecKeyChain:

var record = new SecRecord(SecKind.InternetPassword)
{
    Service = ServiceName,
    Label = ServiceName,
    Account = username,
    ValueData = NSData.FromString(password)
};

SecKeyChain.Add(record);

However, if you remember from earlier – you never want to insert a record without checking for an existing one because you’ll risk adding a duplicate record. Once inserted, you’ll see your password in the keychain utlity:

For code on how to perform a check and then update / insert as necessary, check the sample code.

Updating

Updating involves sending in the existing record (or at least one that’ll uniquely identify the record you’re after), and a new record to replace it:

record.ValueData = NSData.FromString(password);
SecKeyChain.Update(searchRecord, record);

The first parameter is handled in exactly the same was as when querying for an existing record, the second parameter is your newly updated record.

Deleting

Deleting is remarkably similar to updating, except you only send in your search record:

var searchRecord = new SecRecord(SecKind.InternetPassword)
{
    Service = ServiceName,
    Account = username
};

SecKeyChain.Remove(searchRecord);

Finishing Up

This guide has demonstrated how to interact with the OS X keychain using MonoMac. Make sure to check out the sample project for a simple utility class that wraps up all of the necessary functionality with a friendly interface.

Posted by Dan in C#, Mac, Tutorials, 2 comments

iPhone Glyphs from ‘Apple Symbols’ in Lion / Mountain Lion

The Apple Symbols font provides a lot of the symbols used in iOS, however since Lion it’s been harder to get at them. Here is how to use them if you’re on Lion or above:

First open Font Book and open up the Apple Symbols font.

Next, bring up the print dialog (CMD + P).

Now click on ‘Show Details’

Change the Report Type to ‘Repetoire’. Next change the glyph size to something larger, I find 30pt about right most of the time.

Now click on the PDF button in the bottom left, and save the PDF somewhere.

Next, start up Photoshop and drag the PDF into the stage.

You want the last couple of pages for the iOS glyphs. Make sure the resolution is high enough, I like to go for a dpi of 300. Click on OK and you should be presented with the glyphs nicely anti-aliased with a transparent background:

Glyps are ready to go!

Posted by Dan in Guides, iOS, Programming, Tutorials, 0 comments

MonoMac “Exception has been thrown by the target of an invocation” when accessing outlet

If you’re getting a ‘Exception has been thrown by the target of an invocation’ exception when trying to access an outlet variable, it could be the outlet has been hooked up wrong. The outlets must be hooked up to the File Owner, which is most likely to be your controller class. Xcode presented the view rather than the controller in the editor panel, so I had hooked up all of my outlets to the view instead of the controller by accident.

Posted by Dan in Mac, 0 comments

Cocoa Popup window in the Status bar – MonoMac Port

Vadim Shpakovski way back on July 2011 released code for a popup window in the status bar, and now in August 2012 I have ported it over to MonoMac with a few tweaks! If you don’t care about the guide below, you can access the source at Github.

The implementation is largely the same as used in Vadim’s implementation, with a few tweaks to make it simpler, more .NET-like and more extensible. There’s still more work that needs to be done to make it truly modular architecture-wise, but I’ve made the classes themselves easily extensible with clear extension points.

High Level Overview

The entry point is the StatusPanelController, this controller creates the icon in the status bar. It takes in a controller that inherits from PanelController so that you can create your own custom views if necessary. When the user clicks on the icon, the controller will call OpenPanel() and ClosePanel() in the PanelController.

The PanelController is the controller for the panel that opens – it’s responsible for all rendering.

BackgroundView is the background for the panel we display, you can think of it as the panel itself. It renders the arrow, border, and background. For this reason the panel you use must have a BackgroundView, additionally the panel itself should be effectively non-styled, with all standard window chrome removed.

That’s basically it!

BackgroundView

BackgroundView is a special view that represents the popup panel. At the top of the source file are various constants for controlling how the view should be rendered. ArrowX is necessary so that we know where the middle of the status bar icon is. The arrow will centre itself on this point.

To render the view we override DrawRect, and provide our own custom drawing using an NSBezierPath to do most of the work. Take note that the coords .NET use are reversed on the Y axis, .NET’s origin is in the top-left, while OS X’s origin is in the bottom left.

PanelController (and a little note on MonoMac Animation)

PanelController is the most substantial class in the entire project. This controller is responsible for the display of the panel itself, this means it needs to handle the animation of the panel, as well as updating the BackgroundView with the position of the status icon, should the window be resized. HandleWindowDidResize handles the arrow position, updating the BackgroundView whenever the panel is resized. Meanwhile we have two events, WillClose and DidResignKey that we use to know when to close the panel. We don’t want the panel just disappearing, we want a nice smooth animation!

OpenPanel() is responsible for displaying the panel in a smooth way. First of all it works out the position the panel needs to be, this is based on the icon position. Next we set up the panel for display by ensuring critical state, and adding a teeny bit of a debug Easter egg. If you press shift, or shift + option, you slow down the animation, and optionally output debug information relating to the upcoming display.

Finally we animate the panel into view. Note that we cast the Animator property of the window, this is because by default the Animator property is of type NSObject. The Animator is actually a proxy for the full object, so we can just cast it to the ‘real’ type, and set our new post-animation properties. We then finish our animations, and let OS X do the rest!

ClosePanel() is the opposite of OpenPanel() and just animates the panel away.

Panel

Panel is a very slightly customised implementation of NSPanel. We just add an extra property so that we can use controls in the panel that require the keyboard, such as text fields.

StatusItemView

This is the view that will be displayed in the status bar. It’s fairly self-explanatory, with different images depending on the state of the button. There’s a GlobalRect property so that we can locate the icon in the screen and position the panel.

For rendering we override DrawRect, and do it largely ourselves. We first draw the background into the view, optionally highlighted. Next we draw the requisite image (the icon) in the centre of the view. We use an updated method call over the one used by Vadim because the original has been deprecated – it’s not even available in MonoMac!

Finally we track the MouseDown and MouseUp events, rather than just MouseUp. A ‘click’ is considered when the user both mouses down and up, in the same view. If the user does this, we fire the StatusItemClicked event.

StatusPanelController

Finally we have the controller that actually brings it all together. The controller could do with a little more work to make it genuinely re-usable, since it uses hard-coded values for the images, as well as the view used for the status icon. Fortunately re-factoring these out into dependencies would be fairly trivial.

At construction we create all of our required controllers and views, this creates the icon in the status bar.

We hook into the clicked event from StatusItemView, and toggle the panel as neccesary.

Finally we implement IDisposable so that we never leave an icon in the status bar by accident – that would be very bad! We also have a finalizer so that no matter what, the icon will end up removed from the status bar when the controller is garbage collected.

Posted by Dan in Mac, Programming, 6 comments

Running multiple instances of MonoDevelop on Mac

By default OS X will only let you run a single instance of any app, which is OK if the app is designed with multiple windows & a single instance in mind. MonoDevelop is a different beast, and sooner or later you’re going to want to run at least 2 instances of it, here’s how:

First run the AppleScript editor, and enter the following code:

do shell script "open -n /Applications/MonoDevelop.app/"

Now save the script somewhere easily accessible, make sure to set the ‘File Format’ to ‘Application’. I chose the name ‘MonoDevelop Launcher’:

Now the only problem left is the icon, it’s most likely the AppleScript icon, rather than the MonoDevelop one:

Use ‘Get Info’ to get the info for both your new launcher, and MonoDevelop. Click on the icon for MonoDevelop and press CMD+C. Now click on the icon for your script, and press CMD+V. The icon should now be correct:

References: Based on answer by ‘Subfuzion’ as StackOverflow

Posted by Dan in Guides, Mac, Programming, 0 comments

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

Getting Back / Forward mouse buttons working in Mac OS X

If you want to use mouse buttons #4 and #5 to go forward / backward in Mac OS X out of the box you’re going to be disappointed, and end up with just a scroll icon appearing.

There’s a few utilities you can use to fix this:

  • Microsoft IntelliPoint – Only works with MS mice, doesn’t seem to work in Mountain Lion
  • Logitech Control Centre – Only works with Logitech mice, apparently quite buggy now
  • Razer software – not tested, will only work with Razer mice anyway
  • Steermouse – Getting increasingly flakey with new OS X versions
  • USB Overdrive – Not been in development since Snow Leopard, doesn’t seem to work 100% of the time anymore
  • ControllerMate – Works!

ControllerMate to the rescue!

ControllerMate is actively developed & maintained, and offers an incredible amount of customisation for all HID peripherals. So not only will it sort out your back / forward issue, it’ll also solve the mouse acceleration issue – if that’s a problem for you. This guide will cover back / forward only, though.

1. First download and install ControllerMate.

2. Reboot and launch ControllerMate.

You’ll now be presented with a scary as hell screen that looks like you’re laying out circuit boards, rather than fixing a major limitation in OS X.

The diagrams in place on first launch are a kind of mini tutorial, telling you how you can do a few things. You can either leave them in place for future reference, or delete them to clean up the view.

The first step is to create a new Programming Group:

3. To do this, right-click in the sidebar and select ‘Create Programming Group”.

4. Afterwards, right-click on the new group, and select ‘Create Programming Page’.

5. Now right-click on the new group again, point to ‘Create Driver Configuration’, then select your mouse.

You can name your new items in the ‘Inspector’ window to the right.

6. Click on your programming page to display an empty grid.

7. In the ‘Palette’ to the right, select ‘Controllers’ from the drop down list. Find your mouse, then drag button #4, and button #5 to the grid.

8. Now select ‘Output’ from the drop down list in the Palette. Drag an instance of ‘Keystrokes Building Block’ underneath each of your mouse button instances.

You should now have something like this (without the green lines):

9. Link the blocks, by dragging from the button’s green hotspot, to the action’s green hotspot. Just as in the screenshot above.

10. Click on the keystroke block for button #4. In the Inspector window, click on the ‘Keys’ tab, then click on ‘Capture’. Enter they key combination for ‘Back’ (Command + [), then press stop.

If the command doesn’t appear correctly, click on ‘Open Keystrokes Palette’, and drag the buttons from the virtual keyboard to the window, as seen above.

11. Do the same for button #5’s keystroke block, but this time use the command for forward (Command + ]).

12. Click on the driver configuration item in the sidebar, and set buttons #4 and #5 to ‘None’.

You’re now done! Backward / Forward should work immediately in all apps.

But now my VM / Other App doesn’t respond to buttons #4 and #5!

This is a problem as a result of disabling the buttons and remapping them to forward / back. Fortunately there is a solution!

  1. Click on the group in the sidebar
  2. In the ‘Inspector’ window, check ‘Activate items when these applications are:’, select ‘running’
  3. Add all of the apps that you want to add back / forward support to the list.

Now the mouse will work in all applications. If an app supports backward / forward, remember to add it to the list above.

More VM

Not all VMs support the extra mouse buttons, even if they’re receiving them. If you’re having trouble, try following my follow-up guide for Parallels.

Posted by Dan in Guides, Mac, 20 comments