A new direction

There’s been a marginal change in direction since I last posted something regarding my adventures in iOS.

Firstly, thanks to Sean Botha, whom I met for the first time in real life (rather than on Twitter) at last month’s Highland Web Group meetup, I’ve now taken on some traditional web design work for a company in Inverness. This in itself has considerably reduced the time available to me to develop iOS apps.

However, this company has a medium term desire to develop iPhone apps which dovetail with what they’re currently doing for their clients. So all is not lost, because not only may I end up developing apps, but I’ll also be getting paid for doing so. Hoorah! And having apps in the public domain will help further my new career.

To celebrate this win, I convinced myself that it was about time to invest in an iPad. This new work requires me to be out of the office occasionally and hence an iPad for that purpose makes perfect sense.1 Plus, I wanted to see how easy it was to update Alisto for iCloud syncing and this can only be tested on actual devices. So, immediately after I signed my new contract I headed off to Stormfront and purchased a 16GB iPad. Now my wife and I can site side by side on the sofa with our respective iPads; just as it should be.

I’ll have more about iCloud and Alisto in a forthcoming post, but the TL;DR of that will be: it was easy.

  1. Besides, you have no idea (or maybe you do) how annoying it is to unplug / replug a MacBook Air, especially when I forget to unmount my Time Machine backup drive and it goes belly up – which has happened to me twice.

Introducing Trelloid

I tried to love Sprint.ly, I really did, but the biggest problem I had with it was the huge amount of space it uses displaying one item. During my trial period, I frequently created a Story, only to discover one similar to it in my Backlog, and no amount of filtering by tag, or other search criteria gave me the confidence that I was in control.

Thus, I decided to try Trello. This is a free Kanban-style web tool which I have been using for various projects ever since it launched, and it has a rather good iPhone companion application. You can create a board, fill it with lists and each list can have a number of cards:

Alisto | Trello

For reference purposes, each Card in a Trello board has a number which is unique to that board. However, to get at that number you have to view the card’s details. This makes it difficult to easily reference a card in other systems. So, inspired by the Trello Scrum Chrome Extension (which adds storypoints to Trello boards), I wrote my own extension Trelloid, which prepends the card number into the title:

Trello Board with Card Number.jpg

It works as follows:

  • If a Board title has a “#” at the end, then the board will have card numbers prepended, otherwise it will be left alone. This allows you to show card numbers only when necessary.
  • If a Card already has a number (e.g. #1052) at the beginning of its title, then it will be left alone. This allows titles that reference some external system to be unaffected.

It’s my intention to publish this on the Chrome Web Store, but in the meantime, you can get it here.

Remembering a List

Let’s assume for one moment the possibility that Alisto crashes, is terminated by iOS or indeed by the user. Wouldn’t it be nice to remember the most recently viewed List and pop that up on the screen when the application next launches?

Because a user navigates from a list of Lists, to an specific list, in this situation the application needs to push an additional view onto the stack when it launches. But, in the interests of tidyiness, I don’t want the main application delegate to do this work. This is how I’ve done it:

I want to store in NSUserDefaults a reference to the list currently on display, and whenever that changes the reference is updated. NSUserDefaults can only store simple objects, so storing an NSManagedObject is out of the question. However, each NSManagedObject does have a unique URL obtained via its URIRepresentation method.

The first step is to have the controller which displays a list to be record the list on display. This is achieved by updating the viewDidLoad method. As you’ll recall from previous posts, item is the actual list.

NSURL *url = [[item objectID] URIRepresentation];
[[NSUserDefaults standardUserDefaults] setURL:url forKey:[NSString stringWithFormat:@"%d", AlistoPrefCurrentListItemURI]];

The ListsViewController which is responsible for handling the list of lists can now show a list if it’s tapped on, or if commanded to on launch. It’s therefore appropriate to factor out the code which pushes the display of an individual list:

- (void)viewList:(ListItem *)item {
    if (item) {
        ListViewController *listViewController = [[ListViewController alloc] init];    
        [listViewController setItem:item];

        [[self navigationController] pushViewController:listViewController animated:YES];
    }
}

This is invoked either via the tableView:didSelectRowAtIndexPath method (as per usual) or (now) via the viewWillAppear method, which I updated as follows:

- (void)viewWillAppear:(BOOL)animated {    
    [super viewWillAppear:animated];
    [[self tableView] reloadData];

    if ([self pushedListItem]) {
        [self viewList:pushedListItem];
        pushedListItem = nil;
    }
}

pushListItem is a property which references a list object, it is intended to be set by the application delegate to indicate that a list is to be shown immediately. In the above code, as soon as the controller view appears, the controller examines the reference and pushes the list on display via the aforementioned viewList method.

The application delegate’s didFinishLaunchingWithOptions then has the following code which examines the user setting, converts the setting into an actual object, then tells the (just created) controller the list object it should show.

ListsViewController *listsViewController = [[ListsViewController alloc] init];
    
NSURL *url = [[NSUserDefaults standardUserDefaults] URLForKey:[NSString stringWithFormat:@"%d", AlistoPrefCurrentListItemURI]];
if (url) {
    NSError *anyError = nil;
    NSManagedObjectID *objectID = [[CACoreData sharedPersistentStoreCoordinator] managedObjectIDForURIRepresentation:url];
    ListItem *item = (ListItem *)[[CACoreData sharedManagedObjectContext] existingObjectWithID:objectID error:&anyError];
    if (item) {
        [listsViewController setPushedListItem:item];
    }
}