Adoption Curve Dot Net

Creating Image Callouts in Omnigraffle

| Comments

Here’s a quick way of drawing a enlarged callout on an image using Omnigraffle.

To avoid pixellation in the callout, your original image will need to be bigger than the main image on the canvas. Place the main image on the canvas and scale it accordingly:

Draw a circle shape onto the canvas, then add another copy of the main image as the shape’s background using the Set Image option in the Image section in the inspector:

A scaled-down version of the main image will appear as the background of the callout shape:

Now click the Manual Sizing button in the Image inspector to revert the shape background to full size:

Then click the Mask button so that the shape is shown as an overlay on the fullsize image:

Now you can use the scaling grab handles at each corner of the shape’s background image to get the callout to the correct size, and move it so that the correct area is show in the callout:

Clicking the Done button in the Image inspector will hide the full background image – at this point you can move the callout into the right place.

To show the zoomed area, drag another Circle shape onto the canvas and set its fill style to none. Now position it to show the area that’s enlarged in the callout. You can also adjust the border of the circles to make them stand out:

Finally, add two tangent lines to join the original area and the callout:

The end result is a neat callout image highlighting a specific area on the main image.

Universal Principles

| Comments

The UK Cabinet Office is using GDS to run a technology transformation programme, according to their blog. They’ve published the guiding principles that they’re using – and they’re sufficiently flexible to apply pretty much anywhere. Replace specific mentions of government with the sector of your concern, and they’re equally applicable:

Our guiding principles over the next 12-18 months include the following:

* we will start with user needs: until we understand what users across the Cabinet Office want and need, we won’t start buying things

* we will design with choice and flexibility in mind: there will be many and different needs across the department so we will offer technology solutions that fit individuals and teams

* we will be transparent throughout: we will be open about decisions and actions so our users and stakeholders understand why we’re taking a certain approach

* we will architect loosely coupled services: we are not building a “system”; we are delivering a set of devices and services that can be independently replaced. A key success measure for the programme is that we should never have to do it again

* we will favour short contracts: technology changes rapidly and we believe the age of the long-term contract is over. We need to be able to swap services in and out as the need arises

* we will bring the best of consumer technology to the enterprise: modern devices and cloud applications are built to be intuitive and flexible with minimal need for training. We believe business technology should be the same

* we will make security as invisible as possible: we are working with CESG and GSS to ensure all services are secure to new Official level. However, appropriate levels of security shouldn’t get in the way of the user experience of the services

* we will build a long-term capability: technology delivery doesn’t end with the programme. We will not be handing the services over to a single outsource vendor in 2015, but instead will be bringing digital skills back into the department

Introducing ActsAsBeacon

| Comments

iOS7 introduces support for Bluetooth LE (aka Bluetooth Smart). “LE” stands for Low Energy – a Bluetooth LE device has an incredibly low current draw, which means it can potentially operate for extended periods (think months) on nothing more than a coin cell battery.

Apple are using Bluetooth LE to power iBeacons – a beacon is a low-power device that talks Bluetooth LE and can be detected by the CoreLocation stack. An app can be “woken up” by a beacon, and can use the signals from several beacons to obtain location information. Think indoor GPS with a (potential) accuracy of centimetres.

At the moment, the iBeacon spec is under NDA, which means that beacons themselves are hard to come by. You can pre-order Estimotes which look like they’ll be the simple, pretty but expensive option; or try Kontakt devices (not so pretty, still expensive). Or there’s the Redbear Labs BLE Mini if you prefer bare boards and some soldering.

iOS devices of recent vintage can act as beacons, though – so if you just need some beacons for testing, there’s no reason why you can’t grab a handful of iPod Touches or similar and use these. The other advantage of this approach is that configuring the various beacon parameters is much easier with a iOS device than fiddling around with hardware alternatives.

ActsAsBeacon is a tiny app which turns your iOS7 device into an iBeacon, and has a search function to show details of beacons in the vicinity. It will also allow you to configure the service UUID and broadcast parameters so that it’s possible to experiment with iBeacon-enabled apps.

In the next version that I’m currently tinkering with, the app will also provide a configuration interface for BLE Mini boards – Redbear Labs have an app for this, but it’s a bit broken on iOS7. My version allows the Mini boards to be configured over the air once their firmware has been updated to run the iBeacon version.

The app’s available as a GitHub repo, and I’ll be submitting a version to the App Store in a couple of days so that there’s no dependency on Xcode and a Developer Program license.

Removing Storyboards From Xcode 5’s Default Single View App Template

| Comments

The new default single-view application template in Xcode 5 is based on Storyboards rather than the previous view-controller-and-nib-files approach. That’s fine if you like Storyboards, but I don’t – so the first thing I do when starting a new project is rip them out and replace them with the old approach.

This is by way of an outboard brain dump to remind myself of how this is done.

Remove the Main.storyboard file

This can simply be deleted.

Update the ProjectName-Info.plist file

Remove the Main storyboard base file name key.

Create a nib file and link to the project’s view controller

  1. Create a nib file (File –> New –> File –> View)
  2. Update the File's Owner's class to whatever the project’s view controller is called
  3. Link the File's Owner's view outlet to the view object in the nib file

Update the app delegate

  1. Import the project’s view controller’s header file
  2. Update the application:didFinishLaunchingWithOptions: method:
1
2
3
4
5
6
7
8
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    MyViewController *viewController = [[MyViewController alloc] initWithNibName:@"MyViewController" bundle:nil];
    self.window.rootViewController = viewController;
    [self.window makeKeyAndVisible];
    return YES;
}

Testing for Cowards Part 3: Testing the Full Interface

| Comments

Introduction to part 3

This is the third of three posts (part 1 | part 2 )that works thorough the presentation I gave at September’s iOSDevUK conference in Aberystwyth. In the first, I covered the background to test-driven development of the simple traffic lights project I’m using as an example; and looked at building the app’s model layer using a test-driven approach. The second covers testing user interaction by exposing the methods that underlie the interface.

The code and tests can be cloned or downloaded from GitHub: https://github.com/timd/TrafficLightTests.

Testing the lights

Once the model and user interaction is tested, the final piece of the jigsaw is testing that the user interface can be successfully updated by the model. This is a somewhat arbitrary division of testing, and I will probably approach things differently in another project.

Having said that, the model-view-controller structure of the app means that there’s something of a natural division between the way that the user interacts with the model (mediated through the user interface) and the way in which the user interface is updated as a result of the model’s behaviour.

The view controller is responsible for handling the lights code returned by the LightEngine and updating the display accordingly. The code is a decimal version of the binary representation of the lights:

The first set of tests check that the updateLightsForCode: method works correctly:

context(@"when working through the sequence", ^{

    it(@"should respond to the updateLightsForCode: method", ^{
        [[vc should] respondToSelector:@selector(updateLightsForCode:)];
    });

    it(@"should show Rxx Rxx when sent the @164 code", ^{
        [vc updateLightsForCode:@164];

        [[vc.upRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor blackColor]];
    });

    it(@"should show RAx Rxx when sent the @180 code", ^{
        [vc updateLightsForCode:@180];

        [[vc.upRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor yellowColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor blackColor]];
    });

    it(@"should show xxG Rxx when sent the @140 code", ^{
        [vc updateLightsForCode:@140];

        [[vc.upRed.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor greenColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor blackColor]];
    });

    it(@"should show xAx Rxx when sent the @148 code", ^{
        [vc updateLightsForCode:@148];

        [[vc.upRed.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor yellowColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor blackColor]];
    });

    it(@"should show Rxx Rxx when sent the @100 code", ^{
        [vc updateLightsForCode:@100];

        [[vc.upRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor blackColor]];
    });

    it(@"should show Rxx RAx when sent the @102 code", ^{
        [vc updateLightsForCode:@102];

        [[vc.upRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor yellowColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor blackColor]];
    });

    it(@"should show Rxx xxG when sent the @97 code", ^{
        [vc updateLightsForCode:@97];

        [[vc.upRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor greenColor]];
    });

    it(@"should show Rxx xAx when sent the @98 code", ^{
        [vc updateLightsForCode:@98];

        [[vc.upRed.backgroundColor should] equal:[UIColor redColor]];
        [[vc.upAmber.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.upGreen.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downRed.backgroundColor should] equal:[UIColor blackColor]];
        [[vc.downAmber.backgroundColor should] equal:[UIColor yellowColor]];
        [[vc.downGreen.backgroundColor should] equal:[UIColor blackColor]];
    });

});

It’s worth noting here that whereas normally you’d try to write code with the minimum of redundancy consistent with readability, with tests that’s not the case. You want the tests to be as clear as possible, and if that means writing lots of code, well, that’s what copy-and-paste was invented for.

It should be immediately obvious what these tests are about, becasue they’re written out in long form. They could be much more concise with a for-each loop and an array of values to iterate across – but then there would be two cognitive loads: one to understand the mechanics of the test, and one to understand the test itself.

Once the operation of the updateLightsForCode: method is proven, then we can look at hooking it up to the UI:

it(@"should update the lights to RAx Rxx after a first tick", ^{

    [[[vc.upRed backgroundColor] should] equal:[UIColor blackColor]];
    [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
    [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

    [[[vc.downRed backgroundColor] should] equal:[UIColor blackColor]];
    [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
    [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

    [vc didTapStartButton:nil];

    [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
    [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
    [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

    [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
    [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
    [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

    [vc didTapTickButton:nil];

    [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
    [[[vc.upAmber backgroundColor] should] equal:[UIColor yellowColor]];
    [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

    [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
    [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
    [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

});

This tests operation in response to the tick button being tapped once – the next stage is to try the whole sequence:

it(@"should run through the whole sequence correctly", ^{

    [vc didTapStartButton:nil];

    // Run through the whole sequence 25 times

    for(int count=0; count < 25; count++) {

        // RYB RBB
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor yellowColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

        // BBG RBB
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor greenColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

        // BYB RBB
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor yellowColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

        // RBB RBB
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

        // RBB RYB
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor yellowColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

        // RBB BBG
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor greenColor]];

        // RBB BYB
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor yellowColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

        // RBB RBB
        [vc didTapTickButton:nil];

        [[[vc.upRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.upAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.upGreen backgroundColor] should] equal:[UIColor blackColor]];

        [[[vc.downRed backgroundColor] should] equal:[UIColor redColor]];
        [[[vc.downAmber backgroundColor] should] equal:[UIColor blackColor]];
        [[[vc.downGreen backgroundColor] should] equal:[UIColor blackColor]];

    }

});

There’s an element of stress testing in this last test – the sequence is repeated 25 times, which should be enough to expose any edge cases when wrapping back to the start of the state machine sequence. And the point about automated testing here is that you could equally choose to test the same sequence 250, 2,500 or 250,000 times – something that would be virtually impossible with manual testing.

Summary

This is a very simple app with a minimum of moving parts – yet it’s got all the elements needed to make for a complex set of interactions. Functions like locking some buttons in response to tapping others can quickly become convoluted and difficult to test thoroughly with a “trained monkey” approach.

Having a set of automated tests can help by allowing the tests to be exhaustive and completely repeatable. This would come into its own if the app was extended in the future – the original set of tests would immediately expose any areas where new functions broke old ones.

The other big advantage of taking a test-driven approach in my opinion is that it forces you to stop and think about the structure of the app at the right moment in the app’s lifecycle. By testing each element in isolation, it’s possible to be sure that one part works before moving onto the next one.

Although that might not be such a big issue in a “toy” app like this, on larger-scale projects (and particularly ones with multi-developer teams), testing will provide a “comfort blanket” that things work in the way which they’re intended to. That’s preventing a cognitive load that comes with uncertainty about the behaviour of areas of code.

Further reading

There’s no shortage of material about testing online and in books, but much of it suffers from the twin problems of a) approaching testing in a quasi-religious dogmatic “test all the things” approach; and b) being very Java-centric.

Kiwi as a framework is heavily influenced by RSpec, and the canonical reference for this is The RSpec Book by David Chelimsky. Although this is Ruby-centric, it’s a good introduction to the processes involved in behaviour and test-driven development.

Rails 4 In Action builds on the concepts covered in The RSpec Book to build out a working Rails site. Again, this is completely focussed on Ruby and Rails, but it does illustrate the BDD and TDD process extremely well.

For an iOS and Objective-C focussed approach, Graham Lee’s Test-Driven iOS Development is excellent. It doesn’t use Kiwi, but is a great introduction to the concepts of testing and practice using SenTest library.

There are relatively-few Kiwi-specific resources around – the GitHub wiki is increasingly comprehensive; and Test Driving iOS Development with Kiwi is a short iBooks title that covers the basics.

And of course, for all other questions and queries there’s the incomparable resource that is Stack Overflow.