iOS 7’s Subtle UITableView View Hierarchy Changes

While updating one of my oldest apps to iOS 7, I noticed that I was getting an exception when trying to grab a UITableViewCell from a UITableView. The code looked something like this:

- (IBAction)OnSomeReallyImportantStepperChanged:(id)sender
    UIStepper* stepper = sender;

    UITableViewCell* cell = (UITableViewCell*)[[stepper superview] superview];
    UITableView* table = (UITableView *)[cell superview];
    NSIndexPath* pathOfTheCell = [table indexPathForCell:cell]; // ***BOOM***

This code worked in iOS 6 and below, but threw an exception in iOS 7:

[UITableViewCell indexPathForCell:]: unrecognized selector sent to instance

What’s going on here? Well, a quick debugging session made it quite clear. cell is actually a UITableViewCellScrollView, not a UITableViewCell, and table is really a UITableViewCell, so naturally it didn’t have an indexPathForCell method.

The moral of the story is – don’t make too many assumptions about the view hierarchy, because it may change!

Undefined Symbol “_OBJC_CLASS_$_NSManagedObject”

I stumbled across this compilation error while adding Core Data support to my latest app-in-progress.

 Undefined symbols for architecture i386:

“_OBJC_CLASS_$_NSManagedObject”, referenced from:

_OBJC_CLASS_$_Show in Show.o

“_OBJC_METACLASS_$_NSManagedObject”, referenced from:

_OBJC_METACLASS_$_Show in Show.o


Fortunately, it’s an easy fix – my project was missing the Core Data framework. After adding it and rebuilding, everything was fine.

The ridiculous part, though, is that despite adding a new Core Data model to my project, creating an entity, and generating an NSManagedObject subclass, Xcode wasn’t smart enough to actually add the Core Data framework for me.

I’m gonna let you in on a little secret, Apple. If someone adds a new Core Data model to their project, and then adds an entity, and then generates NSManagedObject subclasses, it’s probably because, you know, they want to use Core Data. So just add the add the stupid framework automatically and save us all some headaches.

By comparison, Visual Studio does it right, and automatically adds the necessary Entity Framework references for you when you add a new Entity Data Model. There is no reason Xcode shouldn’t do it too (aside from Apple’s general apathy towards their developer tools, that is).

A git workflow for the app store submission process

When you submit an app to the app store for review, what do you do?

Do you simply stop development on the app until the review comes back? Do you keep working on new features? If your app is denied, do you try to resubmit it with the new features you added since the first submission? Do you try to undo everything you did, fix the issues found in the review, and resubmit? Do you switch over to another app?

The app store review process causes some interesting workflow challenges, but git makes them easy to deal with.

Let’s assume that you’re already using Git and Dropbox (or some other DVCS such as mercurial, and some other form of backup or a networked repository). If you aren’t, you should be, so stop reading this article and go read this one instead.

Let’s also assume that you are familiar with basic DVCS concepts such as branching, merging, and tagging. If you aren’t, the git book is a great place to learn.

So, we are ready to start working on some cool new features. We’ll start by creating a branch for our next version – in this case, version 1.3.

Martys-Mac:WhichEpisode marty$ git checkout -b 1.3
M	Which Episode.xcodeproj/project.xcworkspace/xcuserdata/marty.xcuserdatad/UserInterfaceState.xcuserstate
Switched to a new branch '1.3'

Now we’re ready to make our changes. We’ll fix a few bugs, add some features, tweak some views, and so on. This might take a few commits – that’s totally fine.

...make changes...
git commit -am ...

...make changes...
git commit -am ...

...make changes...
git commit -am ...

Ok, we’re now done version 1.3, and we’ve uploaded the binary to the app store for review. Time to end our work on the 1.3 branch by merging it back in to master, and then pushing our changes to our repo in DropBox. You can also push to your remote repo for safekeeping at any point during development of 1.3, of course.

Martys-Mac:WhichEpisode marty$ git checkout master
Switched to branch 'master'
Martys-Mac:WhichEpisode marty$ git merge 1.3
Updating 41988bd..90adff4
 Default-Portrait@2x~ipad.png                       | Bin 0 -> 18121 bytes
 Default-Portrait~ipad.png                          | Bin 0 -> 5949 bytes

Martys-Mac:WhichEpisode marty$ git push
Total 0 (delta 0), reused 0 (delta 0)
To /Users/marty/Dropbox/Projects/WhichEpisode.git/
   41988bd..90adff4  master -> master

At this point, the branch for 1.3 is still there, but we won’t need to touch it again unless something goes wrong with our submission. However, we are ready to work on our next release, 1.4, so let’s create another branch for it:

Martys-Mac:WhichEpisode marty$ git checkout -b 1.4
Switched to a new branch '1.4'

You can now start working on all the cool new features for your next release.
If 1.3 gets rejected, and you need to apply a fix to it, just switch to the 1.3 branch, make the change, and commit it.

Martys-Mac:WhichEpisode marty$ git checkout 1.3
Switched to branch '1.3'

...fix bug...
git commit -am ....

Re-submit the binary, then switch back to the 1.4 branch and merge in the change, to make sure it stays up to date with any important fixes.

Martys-Mac:WhichEpisode marty$ git checkout 1.4
Switched to branch '1.4'
Martys-Mac:WhichEpisode marty$ git merge 1.3
Updating 90adff4..3152f6d
 0 files changed
 create mode 100644 test.txt

Astute readers may notice that at this point, we have a change in the 1.3 branch that’s not in master. I can live with that, because I know the change will find its way to master once the 1.4 branch is merged, but if you are really worried you can always merge 1.3 to master again.

Martys-Mac:WhichEpisode marty$ git checkout master
Switched to branch 'master'
Martys-Mac:WhichEpisode marty$ git merge 1.3
Updating 90adff4..3152f6d
 0 files changed
 create mode 100644 test.txt

So, that’s pretty much how I use git to work with the app store release process. It lets me submit releases to the app store for review, and then continue development in another branch, without worrying about what to do if my app gets rejected.

This has been working quite well for me, but I am curious as to what everyone else does. Thoughts? Ideas? Ways to improve the process? Do something completely different? Use git-flow? I would love to hear your stories!

Make your UITableViews stand out with alternating row colors

UITableViews are the foundation of many iOS apps. Although they are pretty powerful and flexible, they are also very bland in their default configuration.

A quick and easy way to add some style to your boring UITableViews is to add alternating row colors. This gives them some visual ‘pop’ and improves readibility. No fancy design skills required!

Here is a sample UITableView from 20 Rep Squats. It’s a little bit… monotonous.

iOS Simulator Screen shot 2013-01-12 6.51.28 PM

Let’s spice it up a bit. Open your UITableViewController’s .m file, and insert the following code:

-(void) tableView:(UITableView*)tableView willDisplayCell:(UITableViewCell*)cell forRowAtIndexPath:(NSIndexPath*)indexPath
    UIColor* color = indexPath.row % 2 == 0 ? [UIColor whiteColor] : [UIColor colorWithRed:0.95 green:0.95 blue:0.95 alpha:1.0];
    cell.backgroundColor = color;

All we are doing is specifying that even numbered rows will have a white background, and odd numbered rows will have a nice subtle light grey background. Of course, you’ll want to store your background colors in fields or constants, and not have to regenerate them with colorWithRed:geen:blue: every time, but that’s beside the point of this post.

The result looks like this:

iOS Simulator Screen shot 2013-01-12 6.51.01 PM

And there you have it – with only a few seconds of effort, you can take your UITableViews from blah to beautiful! Or blah to average, at the very least.

iOS Quick Tip – “Storyboards are unavailable on iOS 4.3 and prior”

Another day, another bizzare Xcode error.

While working on my newest app, Gym Calculator, I came across a very strange problem. I had initially set my Deployment Target to iOS 4.3. since I didn’t need any fancy new iOS 6 features. However, I couldn’t build, because my project was using Storyboards, which 4.3 doesn’t support. Xcode helpfully gave me the error message

Storyboards are unavailable on iOS 4.3 and prior

Right, no problem then! I’ll just switch to iOS 5 instead. And… same error. Ok, how about a clean and rebuild? Same error. Restart Xcode? Same error.

After some frustration and head-scratching, I stumbled across a long StackOverflow question discussing the problem. The answer that worked for me – hidden in a sea of less useful answers – was C Fraire’s:

The solution that worked for me was just to delete the 
~/Library/Developer/Xcode/DerivedData directory for my project.

So in case anybody else runs into this problem, here’s your solution! (Or at least one potential solution that did the trick for me)

Xcode 4.5 and ‘file is universal (2 slices) but does not contain a(n) armv7s slice’

After firing up Xcode 4.5 for the first time, adding 4″ iPhone support to 20 Rep Squats, and attempting to run it on my device, Xcode gave me a bizzare error:

file is universal (2 slices) but does not contain a(n) armv7s slice: /Users/Marty/Desktop/Projects/20 Rep Squats/libCorePlot-CocoaTouch.a for architecture armv7s
clang: error: linker command failed with exit code 1 (use -v to see invocation)

So, what gives?

It turns out that Xcode 4.5 adds support for the armv7s architecture, as opposed to the standard armv7 architecture. If you are using a library that has not been compiled for armv7s, and your project is set to armv7s, you will get the above error. In my case, it’s the Core Plot library that is throwing the error.

Fixing the problem is simple. You just need to remove armv7s from the list of valid architectures.

-Select your project from the Project Navigator

-Select your target from the list of targets

-Select the build settings tab

-Find the ‘Valid Architectures’ setting. It should say armv7 armv7s.

-Double-click on it to edit it, select armv7s, and press the button.

The Valid Architectures setting should now look like this:

And that’s it! Your code will now compile correctly.