Archive for the ‘Technical’ Category

 

Using the iOS Simulator’s camera

 
 

When you’re using the iPhone and iPad Simulators to run your development code, you’ll find that they have a significant limitation if you’re building apps that use hardware features like the camera.

If your code tries to use the camera on a Simulator device, it’ll crash – not altogether surprisingly because the Simulator doesn’t have a hardware camera. That gets painful pretty quickly.

As a workaround for a project that I’m working on, I figured it would be useful to have the option to select an image from the Simulator’s library in places where normally the camera would be the default option.

This will also come in useful for handling situations where the camera is already in use – you might have another app running in the background that hasn’t released the camera for some reason.

Here’s the two methods that handles this.

The first uses UIImagePicker's isSourceTypeAvailable to check if there is a camera device available to use. If there isn’t, it presents a popover view containing the device’s Photo library from which an image can be selected. The image, whether chosen or taken, is made available to UIImagePickerController's didFinishPickingMediaWithInfo: method. Here you do whatever you need to do with the image, and then get rid of the popover if you’re using one.

It’s pretty straight-forward – you’ll need a couple of properties defined at the top of your class (one to hold a reference to the UIImagePickerController and another to hold a flag to indicate that you’re using a popover). Then use this takeSimulatorSafePhotoWithPopoverFrame: method whenever you want to take a photo. The method takes a single parameter – a CGRect frame for the popover, so you can place this accurately in your UI.

The second method is a UIImagePickerController delegate method that handles the image which is captured or chosen, then dismisses the popover if required.

Drop the first method in wherever you use the camera, and your code will work on the Simulator without crashing.

-(void)takeSimulatorSafePhotoWithPopoverFrame:(GCRect)popoverFrame {
    
    // Prerequisites:
    //
    // Class requires two properties to be defined:
    //
    // @property (nonatomic, strong) UIImagePickerController *imagePicker;
    // @property (nonatomic) BOOL usingPopover;

    // Load the imagePicker
    self.imagePicker = [[UIImagePickerController alloc] init];

    // Set the sourceType to default to the PhotoLibrary and use the ivar to flag that
    // it will be presented in a popover

    UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    self.usingPopover = YES;
    
    // Check if the camera is available - if it is, reset the sourceType to the camera
    // and indicate that the popover won't be used.

    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        sourceType = UIImagePickerControllerSourceTypeCamera;
        self.usingPopover = NO;
    }

    // Set the sourceType of the imagePicker

    [self.imagePicker setSourceType:sourceType];

    // Set up the other imagePicker properties
    self.imagePicker.allowsEditing = NO;
    self.imagePicker.delegate = self;
    
    // If the sourceType isn't the camera, then use the popover to present
    // the imagePicker, with the frame that's been passed into this method

    if (sourceType != UIImagePickerControllerSourceTypeCamera) {
    
     self.popover = [[UIPopoverController alloc] initWithContentViewController:self.imagePicker];
        self.popover.delegate = self;
        [self.popover presentPopoverFromRect:popoverFrame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];

    } else {

// Present a standard imagePicker as a modal view
        [self presentModalViewController:imagePicker animated:YES];
    }
    
}


- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    
    // This UIIMagePickerController delegate method is called by the image picker when
    // it's dismissed as a result of choosing an image from the Photo Library,
    // or taking an image with the camera

    UIImage *takenImage = [info objectForKey:UIImagePickerControllerOriginalImage];

    // Do whatever is required with the image here
    // ...
    
    // Get rid of the imagePicker, either by dismissing the popover...
    if (self.usingPopover) {

        [self.popover dismissPopoverAnimated:YES];

    } else {

// ...or dismissing the modal view controller
        [self dismissModalViewControllerAnimated:YES];

    }
}
view raw gistfile1.m This Gist brought to you by GitHub.

Where to put the registerNib method in a table view controller

 
 

Here’s a quick workaround for a gotcha if you’re using iOS5′s registerNib: method in conjunction with a table view controller class that’s external to the controller of the view that contains the table.

Scenario: You’ve got a table view in a Storyboard (or standard xib file) which is controlled by a UITableViewController class which is separate to the class controlling the Storyboard (or view). You want to use a custom cell layout contained in a separate xib file.

Problem: In order to use the custom cell laid out in the xib, you want to use the registerNib technique. This needs to be called early on in the lifecycle of the table view’s controller. So where should this be done?

Solution: You need to be careful about which method you place the registerNib call in.

If this is done in the table view controller’s init method, the controller’s tableView property won’t have been set. As an external table view controller, the viewDidLoad method won’t get called because the controller isn’t actually managing a view.

A workaround is to create a separate method in the table view controller – say something like -(void)registerCell – and call this after the table view controller has been instantiated.

Here’s an example which sits in the Storyboard controller’s viewDidLoad method:


self.tableController = [[MyTableController alloc] init];
self.tableView.delegate = self.tableController;
self.tableView.datasource = self.tableController;
self.tableController.tableView = self.tableView;
[self.tableController registerCell];

Creating Table Views With Storyboards

 
 

Storyboards are one of the many new features introduced in iOS5 and Xcode 4.2 and take the visual creation of iPhone and iPad interfaces to the next stage.

Although Interface Builder provides a visual way of laying out user interfaces, up until now the process of moving between screens has required a certain amount of “glue code”.  This is relatively straight-forward when you’re dealing with simple apps – but add UIKit functionality like tab bar controllers into the mix, and things suddenly get a lot trickier.  Storyboards  come to your rescue in situations like this.

Although you don’t need to use them, Storyboards can be useful when building table-based applications.  To follow on from my book, I’ve put together quick start guide that steps you through the basics of Storyboards, and how they can be used to build table-based navigation apps.

Download the guide as a PDF

 

How I want my TV remote to work

 
 

This is what my TV remote control looks like (with an Apple keyboard for scale).

It’s horrible. It’s covered with buttons that never get used, and so many are crammed onto the device that they’re too small to use easily. The whole thing is just the right size and shape to fall down between the sofa cushions, too.

The things I use the remote for (in order of priority) are:

  • power on and off
  • channel selection
  • volume adjustment
  • mute on/off
  • changing aspect ratio

The other frustration is that I’m driving an interface that’s 3 metres away – and every onscreen interface I’ve ever seen is butt-ugly.

My iPad, on the other hand, spends a lot of time sat on the sofa next to me. And its interface is anything but butt-ugly.   So this is what I want my TV remote to look like. ]

No on-screen controls on the TV itself – all five functions are put front and centre on the iPad.

 

 

There are four main areas:

the basic controls

Power, volume, mute and aspect.

the channel area

This shows the channel logo and the current programme being shown. It’s a scrollable list – tapping and holding the channel logo will switch channels. The current channel is highlighted.

the schedule area

This scrolls around in tandem with the channel view. Tapping on the programme reveals a pop-up with programme details pulled from the channel guide.

 

the PVR area

This is a scrollable ribbon of programmes that have been recorded – tapping on the programme will reveal a pop-up with player controls. Tapping play will fire up the programme.

The second screen

The “second screen” view is for combining things like the red button functions (although I hardly ever use these) and online activity like Twitter, or Wikipedia or even the broadcaster’s site, if any of them ever got around to doing second screen activities “properly”.

 

Things that I’m not entirely sure about:

  • how switching between the main view and “second screen” view would work.
  • how and where the “red button” functions would best live.

All of this is predicated on the TV being completely slaved to the remote, of course. So it could happen one of two ways – either a TV manufacturer comes up with a model that has a corresponding app; or some other device sits in between and mediates between the iPad and the TV.

Presumably that would translate commands over wifi from the iPad into IR commands to the TV.

I’d buy something like this. Is anyone going to make it?

[Caveat - this is based on half-an-hour of sketching, and some hacking around with Omnigraffle.  No, it's not polished, or the product of extensive UX workshops. But it's still better than that lump of gray plastic crap that came with my TV.]

More Clear cleverness: showing new screens

 
 

Another trick of the Clear app is the way that it takes the established pull-to-refresh interaction of a table view, and extends it to present new screens. So pulling the to-do items table down will first add a new item at the top of the table, then causes the lists table to slide down from the top.

Doing the same in the Lists table results in the Settings table appearing in the same way. The reverse is also true – pulling the table “up” from the bottom will result in the previous screen sliding up from the bottom.

This works by exploiting a property of UIScrollView, from which UITableView inherits. The contentOffset property measures how far from the top of its frame the table has been scrolled – it’s measured in pixels, so if a 1000-pixel tall tableView was scrolled halfway, the contentOffset would be 500px.

The trick here is that the contentOffset can also be negative – which occurs when the tableView’s contents are dragged down from the top to leave a gap. Often this triggers the pull-to-refresh methods, but in Clear’s case it’s the cue for new views to be presented.

To catch the user pulling the table down in this way, you can use the scrollViewDidEndDragging:willDecelerate: method which gets called when the user lifts their touch after dragging the table around.

If you check the tableView’s contentOffset at this point and discover that it’s more than, say, minus 60 pixels, you can use that to trigger another method – be it calling a data update, or pushing in new views. An example might look something like this:

-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    
    CGFloat contentOffsetY = scrollView.contentOffset.y;
    
    if (contentOffsetY < -60.0) {
        [self displayActivitySpinner];
    }
        
}

Detecting a pull at the bottom of the table is similar – instead of looking for a negative contentOffset, though, you need to detect a contentOffset that’s greater than the tableView’s contentSize.

Again, you’ll need to apply a threshold to this to prevent the new view or whatever being presented as a result of a ‘normal’ scroll action. If the tableView is flicked to the bottom, the contentOffset will exceed the contentSize as the table “bounces”.

What Clear seems to do is have three tableViews loaded at any given time, and then change their frames in a UIView animation block to animate them sliding from the top and bottom of the screen.

It’s clever, although it does rely on the user discovering these interactions – there’s no obvious call-to-action as there would be in the case of a “back” button, for example.

How to do Clear-style swiping in iOS tables

 
 

The latest app that everyone’s raving about is Clear – if you haven’t seen it, it’s basically a task list, but one with a rather interesting take on the interface. Instead of the usual tab-bar-and-table-vew setup, the table is full-screen and makes extensive use of gestures.

Deleting and completing tasks are done by swiping the row – a left swipe deletes the item, a right swipe marks it as complete. Here’s the basis of how that’s done.

The effect relies on the cell’s contentView having two UIViews “stacked” one on top of the other, and a pair of UISwipeGestureRecognizers attached to the cell – one for left swipes, and one for right swipes. When a swipe is detected, the uppermost UIView is animated left or right to “reveal” the UIView that sits “underneath”. By chaining the animations together with UIView animation blocks, you can also do things like make the animations “bounce” at each end of their range of movement.

The outline process runs like this:

  1. Create a UITableView and wire up with delegate and data source as per usual.
  2. Create a custom UITableViewCell class, and override the initWithStyle:reuseIdentifer: method to lay out the UIViews inside it. Then implement the animation methods in the custom cell’s subclass, and add the UISwipeGestureRecognizers.
  3. Populate your table with instances of your custom UITableViewCell.

The first and third steps are pretty routine, so don’t really need much explanation. The magic, such as it is, happens within the UITableViewCell subclass. The initWithStyle:reusedIdentifier: method can be used to lay out the contents of the cell and set up the UISwipeGestureRecognizers – here’s a sample method:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code
        
        // Create the top view
        _topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.contentView.frame.size.width, 80)];
        [_topView setBackgroundColor:[UIColor whiteColor]];
        
        // Create the top label
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 20, 150, 40)];
        [label setFont:[UIFont fontWithName:@"Zapfino" size:18]];
        [label setTextColor:[UIColor blackColor]];
        [label setText:@"Swipe me!"];
        [_topView addSubview:label];
        
        // Create the top image
        UIImageView *pointImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"point"]];
        
        CGFloat pointImageXposition = self.contentView.frame.size.width - 160;
        [pointImage setFrame:CGRectMake(pointImageXposition, 18, 144, 44)];
        [_topView addSubview:pointImage];
        
        // Create the swipe view
        _swipeView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.contentView.frame.size.width, 80)];
        [_swipeView setBackgroundColor:[UIColor darkGrayColor]];
        
        // Create the swipe label
        UILabel *haveSwipedlabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 25, 200, 30)];
        [haveSwipedlabel setFont:[UIFont fontWithName:@"GillSans-Bold" size:18]];
        [haveSwipedlabel setTextColor:[UIColor whiteColor]];
        [haveSwipedlabel setBackgroundColor:[UIColor darkGrayColor]];
        [haveSwipedlabel setText:@"I've been swiped!"];
        [_swipeView addSubview:haveSwipedlabel];
        
        // Add views to contentView
        [self.contentView addSubview:_swipeView];
        [self.contentView addSubview:_topView];
        
        // Create the gesture recognizers
        UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(didSwipeRightInCell:)];
        [swipeRight setDirection:UISwipeGestureRecognizerDirectionRight];
        
        UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(didSwipeLeftInCell:)];
        [swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft];
        
        [self addGestureRecognizer:swipeRight];
        [self addGestureRecognizer:swipeLeft];
        
        // Prevent selection highlighting
        [self setSelectionStyle:UITableViewCellSelectionStyleNone];

    }
    return self;
}

view raw swipeCell.m This Gist brought to you by GitHub.

This is fairly verbose, but breaks down into four stages:

  1. creating the top view, which is the one which is viewable when the cell is in the “normal” state
  2. creating the bottom view, which is the one that gets revealed when the top view moves
  3. adding these two views to the cell’s contentView
  4. creating and adding two UISwipeGestureRecognizers to the cell and using these to fire the animation methods.

The animation is done by two methods in the cell subclass, one for right and one for left. Here’s the detail of the rightward-swipe method:

-(IBAction)didSwipeLeftInCell:(id)sender {
    [UIView setAnimationCurve:UIViewAnimationCurveEaseIn];
    [UIView animateWithDuration:1.0 animations:^{
        [_topView setFrame:CGRectMake(-10, 0, 320, 80)];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.15 animations:^{
            [_topView setFrame:CGRectMake(0, 0, 320, 80)];
        }];
    }];
}

It uses UIView's block-based animatedWithDuration:animations: method to chain together a series of moves to give a “bounce” effect – the view that’s doing the movement “overshoots” the end position slightly, and then settles back. The durations in this code are deliberately long to show the effect, but when they’re shorted in practice the bounce effect can really liven up the interface.

In the case of Clear, the swipe gesture methods also trigger updates to the tableView’s data model to mark the task as completed (or deleting it). At a rough guess, there’s a separate section of the table for deleted items that uses another custom UITableViewCell subclass to style the items with the strikethrough and dark background.

The swipe effects are a nice detail in the Clear app, and it’s a great example of how gestures can be used in place of more traditional tap-based interactions. The tradeoff is that the interactions are less apparent – the call to action of a Delete button is obvious, whereas a gesture has to be either explicitly explained or discovered. It’ll be interesting to see if this is the start of a pattern in app design.

[I've put an example app that's a full implementation of the swipe interactions shown above up on GitHub at https://github.com/timd/SwipingTable . And the full process is shown in detail in my book, for which this is a shameless plug (well, if I can't promote it on my own blog, where can I? ;-) ]

Sliding clocks around the screen a la Path

 
 

The new Path app has hit the App Store, and the updated UI is GORGEOUS.   I was quite intrigued by the little touches, particularly the little clock icon that slides up and down the right-hand side of the screen as you scroll down the timeline. It looks really neat, and it got me wondering how it was done.

Turns out, it’s actually not that difficult (although it’s still a lovely UI touch even so).

The main view in Path is a heavily-customised UITableView sat inside a UINavigationController (although the UINavigationController bit is irrelevant at the moment). UITableViews are themselves heavily-customised subclasses of UIScrollView, which means that all the goodies that a UIScrollView provides are available to instances of UITableView.

There’s one particular goodie that’s relevant here. This is UIScrollView's contentOffset property, which shows how far from the origin the scrollView – or in this case, our tableView – has been scrolled. When the table’s at the top, the contentOffset's y value will be zero – but if you had a table that was (say) 1000 pixels high, that contentOffset would gradually increase until at the very bottom of the table it had a y value of 1000 pixels.

Another property that’s relevant here is the tableView’s contentSize property. If you access that after the table has finished loading – ie in the viewDidAppear method – then this will return the total height of the table with all rows fully-populated.

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
 
maxTableHeight = self.tableView.contentSize.height;
frameTableHeight = self.tableView.frame.size.height;
workingTableHeight = maxTableHeight - frameTableHeight - 64;
 
NSLog(@"TableView maxTableHeight = %f", maxTableHeight);
NSLog(@"TableView frameTableHeight = %f", frameTableHeight);
NSLog(@"TableView workingTableHeight = %f", workingTableHeight);
 
}

Dividing the contentOffset.y value by the contentSize.height will give you how far down the tableView things have scrolled as a percentage value. When the table first loads, the contentOffset.y value will be zero, therefore we’d be 0% down the table. At the bottom, we’d be 100% of the way – and obviously varying percentages along the way.

The third value that will help out here is the height of the table’s frame – in other words, how much of the overall screen the tableView occupies. If you’ve got a status bar and a navigation bar, that’s the full height of an iPhone screen – 480 pixels – less 20 pixels for the status bar and 44 pixels for the navigation bar – a total frame height of 416 pixels.

Now imagine that you created a UIView of some description, and “floated” it as the front-most view so that it appeared on the right-hand edge of the screen. (If it’s a subView of the main window, then the origin remains constant and doesn’t move with the table itself.) It’ll have an origin with an Y position and a Y position – x will be somewhere between 0 and 320, depending on how far right it needs to be placed. And the Y position will control how far up or down the screen it appears.

If we calculate the Y position by taking the height of the tableView's frame and multiplying it by the percentage that the table has been scrolled, the UIView will appear to move up and down the screen in proportion to how fat the tableView has scrolled. It’ll start at the top of the screen when the tableView is at the top, and slide down towards the bottom as the tableView is scrolled. It won’t leave the visible screen regardless of how long the tableView actually is, because we’re calculating the percentage of the tableView's frame rather than its content size.

Because UITableView inherits from UIScrollView, it means we can use the scrollViewDidScroll: method of UIScrollViewDelegate as the “trigger” for calculating how far things have scrolled, and adjusting the position of the UIView with the little clock icon.

-(void)scrollViewDidScroll:(UIScrollView *)scrollView {
 
// Work out positions
currentTablePosition = scrollView.contentOffset.y;
currentTablePositionPercentage = (currentTablePosition / workingTableHeight);
 
// Work out position of "block"
float blockOffset = (frameTableHeight * currentTablePositionPercentage);
 
float newYpos = 64 + blockOffset;
 
NSLog(@"tableView's scroll position = %f", currentTablePosition);
NSLog(@"blockOffset position = %f", blockOffset);
NSLog(@"current table position %%age = %f", currentTablePositionPercentage);
 
NSLog(@"New xpos = %f", newYpos);
 
theBlockView = theAppDelegate.theBlockView;
theBlockView.frame = CGRectMake(theBlockView.frame.origin.x, newYpos, theBlockView.frame.size.width, theBlockView.frame.size.height);
 
}

This is all *very* crude, not least because the calculations don’t take into account the size of the UIView that you’re moving around. Nor do they account for the fact that you can scroll a tableView or scrollView *beyond* 0% and 100% as it bounces at the top and bottom extents.

Path also takes things one stage further by figuring out which row the little icon thing is hovering over, and changing the value of the clock to reflect when the contents of that row were updated. There’s also some very neat animation as the clock times change by spinning the hands. But the basic principles of figuring out where the icon slider should be still hold.

UpdateFlorian Mielke has produced a much better explanation (and a library on Github) here

Accessing controls in UIView subviews

 
 

The iPhone app I’m working on at the moment has a number of screens that consist of long list of questions – so the user needs to be able to scroll down to the bottom.

At the same time, there’s some information at the top of the screen which needs to stay visible. Within the subview containing the questions, there are also several buttons which need to trigger methods in the main view controller – save the answers, move to the next screen and so on.

I’ve done this placing a scrollview in the bottom 2/3 of the screen; and placing all the questions and their associated images into a subview which gets loaded into said scroll view.

The easiest way of putting together the questions view was as a separate nib, but that then posed the question “how to access the controls inside the nib once it’s loaded?” The answer actually seems fairly straight-forward once I figured it out.

The trick is to exploit the fact that controls like UIButtons, UITextLabels, UIImageViews and so on all inherit from the UIView parent class. That means they inherit the tag property – an integer value associated with that particularly control instance that allows you to uniquely identify it from within the overall hierarchy of views.

When creating the nib in Interface Builder, put it together as you would a normal view – but every control that you want to access from the main view controller needs to have a unique tag set.

I tend to start numbering them at 1010 and work up in 10s from there – which reminds me of the old Basic days when programs would start “10 print “hello”…

To get the custom view displayed in the scrollview, you first have to load it. That’s done with:

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"scrollViewNib" owner:self options:nil];

This loads the nib and places its view objects into an array called nibObjects. As far as I can tell, the index of each UIView object in the array corresponds to its place in the hierarchy of views in the nib – which makes the view object at index 0 the main view object into which all the others have been placed.

Once you’ve got the array, you can get a reference to the main view of the nib through the object at index 0:

UIView *nibView = [nibObjects objectAtIndex:0];

At this point you’ll want to add it into the scrollview:

[scrollview addSubview:nibView];

and set the scrollView’s content size to the dimensions of the nibView:

[scrollView setContentSize:CGSizeMake(nibView.frame.size.width, nibView.frame.size.height);

Now you can access the individual buttons, labels, imageviews and so on by exploiting their having the aforementioned tag property. So getting a reference to a UIView subclass with the tag 1050 would be done with:

UIView *aButtonView = [nibView objectWithTag:1050];

That gives you a generic UIView object, which although helpful isn’t the full story. To access the properties of the control itself, you’ll first need to cast the generic object into the class that you’re trying to work with. So instead of the line above, you actually need:

UIButton *aButtonView = (UIButton *)[nibView objectWithTag:1050];

Now you’ve got a bona-fide UIButton, it’ll respond to button methods such as

[aButtonView addTarget:self action:@selector(aButtonTapped) forControlEvent:UIControlEventTouchUpInside];
[aButtonView setEnabled:YES];

and so on. Set each control’s methods as required, and away you go.

Unit testing Core Data

 
 

The project I’m working on at the moment is Core Data-based, and I’m (attempting) to built it using a test-driven approach. In practice, there’s at least as much development-driven testing as there is test-driven development, but I’m using the fact that I’m on a learning curve as my get-out-of-jail-free card.

I’m using GHUnit and OCMock as my test frameworks. Testing the view controllers and so on is fairly straight-forward so far, but I hit a major bump in the road when I started to try and test the Core Data related parts.

While the app itself was working fine, the tests steadfastly refused to return any data. No errors, just empty result sets.

What I’d omitted to do in my testing frenzy was set up the Core Data environment correctly in my tests. You can’t, as it turns out, rely on the app code creating it – you have to do so explicitly within your test suite.

For the benefit of my outsourced memory and any subsequent Google hits, this is how I got it to work.

Firstly, you need to import the CoreData headers:

#import &lt;CoreData/CoreData.h&gt;

and the headers for your NSManagedObject classes:

#import "Trial.h"

Then, add and synthesise a property to hold the Managed Object Context when it’s created:

@property (nonatomic, retain) NSManagedObjectContext *moc;
@synthesize moc;

Because I’m using GHUnit, the methods in my test templete looks like this:

-(void)testSomething {
  // This test should do something eventually
}
 
#pragma mark - Housekeeping
 
- (BOOL)shouldRunOnMainThread {
    // By default NO, but if you have a UI test or test dependent on running on the main thread return YES.
    // Also an async test that calls back on the main thread, you'll probably want to return YES.
    return NO;
}
 
- (void)setUpClass {
    // Run at start of all tests in the class
}
 
- (void)tearDownClass {
    // Run at end of all tests in the class
}
 
- (void)setUp {
    // Run before each test method
}
 
- (void)tearDown {
    // Run after each test method
}

The key methods are setupClass and tearDownClass, which get run at the start and end of the test suite. These is where you create and destroy the Managed Object Context:

- (void)setUpClass {
    // Run at start of all tests in the class
    NSManagedObjectModel *mom = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]];
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
 
    GHAssertTrue((int)[psc addPersistentStoreWithType:NSInMemoryStoreType configuration:nil URL:nil options:nil error:NULL], @"Should be able to add in-memory store");
 
    self.moc = [[NSManagedObjectContext alloc] init];
    self.moc.persistentStoreCoordinator = psc;
 
    [self createSeedData];
}

There’s a subtlety when creating the managed object model – this needs to load all bundles, hence the [NSBundle allBundles] parameter.

- (void)tearDownClass {
    // Run at end of all tests in the class
    self.moc = nil;
}

This creates a sandbox MOC which you can mess about with to your heart’s content – doing things like seeding a data set:

-(void)createSeedData {
 
    NSArray *dataArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", @"Four", @"Five", @"Six", @"Seven", @"Eight", nil];
 
    for (int i=0; i &lt; [dataArray count]; i++) {
 
		Trial = [Trial alloc] initWithData:[dataArray objectAtIndex:i];
 
		// Set up each trial - 4 get "inprogress" status
                // and 3 get "completed" status
 
	}
 
}

What I’m doing here is creating 8 Trial objects, and setting 4 of them as in progress and 3 as completed (the detail isn’t that relevant for this example). This gets used later to test that the predicates selecting the Trials is working correctly.

From here, it’s simple enough to start testing (because I’m paranoid, I added a test to check that the seed data was created correctly):

-(void)testSeedDataIsCreatedCorrectly {
 
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Trial" inManagedObjectContext:self.moc];
    [fetchRequest setEntity:entity];
 
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"id" ascending:NO];
    NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
 
    [fetchRequest setSortDescriptors:sortDescriptors];
 
    NSError *error = nil;
    NSMutableArray *mutableFetchResults = [[self.moc executeFetchRequest:fetchRequest error:&amp;error] mutableCopy];
    if (mutableFetchResults == nil) {
        NSLog(@"TrialSelectionVC: executeFetchRequest error: %@", [error localizedDescription]);
    }
 
    int resultsCount = [mutableFetchResults count];
    GHAssertEquals(resultsCount, 8, @"should be 8 objects in the test data, found %d", [mutableFetchResults count]);
}

Testing the relevant bit of the view controller means injecting the test MOC into the class. In the production code, the MOC that’s assigned to the view controller’s managedObjectContext is the one that gets created in the AppDelegate – which obviously I don’t have here:

-(void)testTrialsVCretrievesData {
    // Create and fetch trials
    TrialSelectionVC *trialVC = [[TrialSelectionVC alloc] init];
 
    trialVC.managedObjectContext = self.moc;
 
    NSMutableArray *results = [trialVC fetchTrials];
 
    // Assert results are returned
    GHAssertNotNil(results, @"results array is nil");
 
    // Assert that there should be three results
    GHAssertEquals((int)[results count], 3, @"there should be 3 results, %d were returned", [results count]);
 
}

On the face of it, this seems like an awful lot of code just for testing purposes – but it’s paid back as far as I’m concerned. By being able to extract and test the data selection elements of the application in isolation, I’m not having to do that by running the app itself.

It also means that I can afford to bugger about with my data schema, and be able to get an instant check on whether I’ve broken things downstream. In an ideal world that wouldn’t happen anyway, but at least now I’ve got some assurance that the app is somewhat less brittle than it might otherwise be.

Why won’t my *&%^$&* Magic Mouse work?

 
 

If you’ve a) recently upgraded to Lion; and b) are shouting this a lot every time you wake your Macbook Pro up from sleep, I’ve got a fix for you.

The symptoms are that once you’ve woken the Macbook up, your Magic Mouse has been disconnected.   Clicking the ‘connect’ option from the Bluetooth menu apparently connects to the mouse, but nothing happens.  The only way to get it to work again is to open the mouse preferences pane, at which point it starts to work again.

I’ve no idea WHY this was happening – there’s nothing in the log files to suggest a problem, but I have got a fix.  Power down the machine, then reset the System Management Controller.  That’s done by holding down the left-hand shift, control and option keys while powering on.  Release the shift/control/option keys as the machine boots back up again.

That should nail the problem.  Normal post-wakeup mouse service will be resumed.

 
 

About

Hello, I'm Tim. I'm a geek who builds online and mobile software and also takes photographs and messes around with technology. This is my personal website.

You can find out more about me and browse through the full archives. I also take photographs, and hack around with things. You can find me elsewhere on the interwebs, get in touch, or subscribe to a feed from this site.

Creative Commons License
The site's content is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.0 UK: England & Wales License, unless indicated otherwise. Please don't steal it :-)

About this site

This site is powered by Wordpress, and hosted by the nice people at Linode. The template is custom-built and based on 960bc and the 960 grid.