Framed in an interview!

Recently I have been interviewing and it is eye opening to be the other side of the interview table/Zoom chat! Having been an iOS developer for 14+ years, I think there are things you learn very early on in your career that you never question or need to revisit it again and it can catch you a little off-guard. One of those areas that I got asked about recently was about the relationship between the frame, bounds and transforms in a `UIView`.

Continue reading

5 Time-saving Objective-C tips every developer should know

Batman Flat White Coffee

Here’s a quick fire list of 5 time-saving Objective-C tips that every developer should know. Perfect for making time for that extra coffee!

1. Enum shorthand

Enums at their simplest are labelled sets of integers. Where an inexperienced programmer might use a number to represent the download state of an image lets say, e.g. 0 = queued, 1 = downloading, 2 = downloaded, 3 = complete… When using these numbers in actual code a simple slip of the finger on the keyboard and they could easily type an extra digit in their if statement or assignment. These types of errors don’t usually show up when compiling. Even worse, these are usually obscure errors that only reveal themselves at run-time and only then when things don’t work as expected often requiring considerable debugging effort.

This is where enums step in. Enums are really handy and allow you to associate symbols or ‘labels’ with integers. If you type an enum value that doesn’t exist the compiler steps in and reports an error at compile-time instead and Xcode will probably suggest a fix for you if it was a typo! The other benefit of enums is that you can quickly add new values into them and assuming your code doesn’t save the integer to disk your code will automatically work with the new values.

There are a couple of different ways to define enums but the easiest way which will also provide Xcode with some extra compiling hints is as follows:

Continue reading

WWDC Update: 5 key points for iPhone apps

It’s now been just over 3 weeks since the torrent of information unleashed at Apple’s World Wide Developer Conference refreshingly drenched the brains of designers, developers and engineers. I’ve resisted blogging about the public announcements to fully let the impact soak in and gage everyone’s reactions but now feels like a good time to talk about where the future of computing is heading.
Continue reading

Calculating UNIX file permissions

Permissions Mac AppA few years ago I wrote a simple but handy Mac app that calculates unix file permissions using a matrix of check boxes. I wrote it because I wanted to better understand how those octal values get calculated and to expand my experience of writing Mac apps.

I was also learning some crazy assembly code at the time too so I was also making sense of putting bitwise operations to task. Rather than let this code languish on my hard drive, I thought I’d share…
Continue reading

Core Data Objects in Wrong Sections

NSFetchedResultsController is a really handy class. Use one of the default Core Data templates in Xcode and you’ll very quickly have a nice list of managed objects in a table view. With a few more lines of code you can get the NSFetchedResultsController to group your objects by sections. You do this by specifying a key-path in the class’s constructor method but there is another step that if overlooked will cause some confusion.

In a sample app I’ve created a food table that lists food in categories.

FetchedResultsController method grouping sections using a key-path:

Objective-C:

- (NSFetchedResultsController *)fetchedResultsController {
	if (fetchedResultsController != nil) {
		return fetchedResultsController;
	}

	// Create and configure a fetch request with the food entity.
	NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
	NSEntityDescription *entity = [NSEntityDescription entityForName:@"RWFood" inManagedObjectContext:managedObjectContext];
	[fetchRequest setEntity:entity];

	// Create the sort descriptors array.
	NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
	NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:nameDescriptor, nil];
	[fetchRequest setSortDescriptors:sortDescriptors];

	// Create and initialize the fetch results controller.
	NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"category" cacheName:@"Food"];
	self.fetchedResultsController = aFetchedResultsController;
	fetchedResultsController.delegate = self;

	// Memory management.
	[aFetchedResultsController release];
	[fetchRequest release];
	[nameDescriptor release];
	[sortDescriptors release];

	return fetchedResultsController;
}

Swift:


lazy var fetchedResultsController: NSFetchedResultsController<rwfood> = {
    let fetchRequest: NSFetchRequest<rwfood> = RWFood.fetchRequest()
    let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]
    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: "category", cacheName: "Food")
    aFetchedResultsController.delegate = self
    do {
        try aFetchedResultsController.performFetch()
    } catch let error {
        print("Unable to perform fetch: \(error)")
    }
    return aFetchedResultsController
}()

Specify a key-path

Screenshot of Food sample app in wrong order.Save and quit the app a few times and you’ll see the objects seem to be in the wrong sections. If you look closer you’ll see that the objects are actually sorted in ascending name order. On looking at the code, it seems this is exactly what we asked the program to do! After some testing it also seems to show up more often if the table is a grouped one.

As per the docs, after you specify a key-path to group each section with you also need to make sure the first sort descriptor is sorting this key-path. Add a sort descriptor and everything will work as expected.

Revised fetchedResultsController method with missing sort descriptor:

Objective-C:

- (NSFetchedResultsController *)fetchedResultsController {

	if (fetchedResultsController != nil) {
		return fetchedResultsController;
	}

	// Create and configure a fetch request with the plant entity.
	NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
	NSEntityDescription *entity = [NSEntityDescription entityForName:@"RWPlant" inManagedObjectContext:managedObjectContext];
	[fetchRequest setEntity:entity];

	// Create the sort descriptors array.
	NSSortDescriptor *typeDescriptor = [[NSSortDescriptor alloc] initWithKey:@"type" ascending:YES];
	NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
	NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:typeDescriptor, nameDescriptor, nil];
	[fetchRequest setSortDescriptors:sortDescriptors];

	// Create and initialize the fetch results controller.
	NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:@"type" cacheName:@"Plants"];
	self.fetchedResultsController = aFetchedResultsController;
	fetchedResultsController.delegate = self;

	// Memory management.
	[aFetchedResultsController release];
	[fetchRequest release];
        [categoryDescriptor release];
	[nameDescriptor release];
	[sortDescriptors release];

	return fetchedResultsController;
}

Swift:


lazy var fetchedResultsController: NSFetchedResultsController = {
    let fetchRequest: NSFetchRequest<rwplant> = RWPlant.fetchRequest()
    let typeDescriptor = NSSortDescriptor(key: "type", ascending: true)
    let nameDescriptor = NSSortDescriptor(key: "name", ascending: true)
    fetchRequest.sortDescriptors = [typeDescriptor, nameDescriptor]
    let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: "type", cacheName: "Plants")
    aFetchedResultsController.delegate = self
    do {
        try aFetchedResultsController.performFetch()
    } catch let error {
        print("Unable to perform fetch: \(error)")
    }
    return aFetchedResultsController
}()