Tewha.net Mostly about iOS.

Built-in categories

NSIndexPath is easier to use than you might think.

If you read the documentation for the class, you’ll see this:

Creating Index Paths

+ indexPathWithIndex:

+ indexPathWithIndexes:length:

- initWithIndex:

- initWithIndexes:length:

- init

Yes, you can use these to construct an index path. But you probably don’t want to. UIKit defines a category on NSIndexPath specific to UITableView’s needs:

// This category provides convenience methods to make it easier to use an NSIndexPath to represent a section and row
@interface NSIndexPath (UITableView)
+ (NSIndexPath *)indexPathForRow:(NSInteger)row inSection:(NSInteger)section;
@property(nonatomic,readonly) NSInteger section;
@property(nonatomic,readonly) NSInteger row;
@end

There’s a similar category defined by UICollectionView:

@interface NSIndexPath (UICollectionViewAdditions)
+ (NSIndexPath *)indexPathForItem:(NSInteger)item inSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);
@property (nonatomic, readonly) NSInteger item NS_AVAILABLE_IOS(6_0);
@end

The absence of section in this category isn’t significant; the same property is already defined by NSIndexPath (UITableView).

UIKit defines other categories, such as drawing categories on NSString.

Why are these separated? Well, NSIndexPath is part of Foundation. UITableView is part of UIKit. String drawing is added to NSString which is part of Foundation, but Foundation, but uses UIFont which is part of UIKit. In short, there’s good technical reasons for this to be splatted out. But knowing that is not helpful to us developers.

So how do you find out about these methods? I don’t think there’s a good way to find categories like these in Apple’s class references. Some of them are mentioned in Apple’s less reference-like material. For instance, there’s ”Collections Programming Topics: Index Paths: Storing a Path Through Nested Arrays”:

In iOS, UITableView and its delegate and data source use index paths to manage much of their content and to handle user interaction. To assist with this, UIKit adds programming interfaces to NSIndexPath to incorporate the rows and sections of a table view more fully into index paths. For more information, see NSIndexPath UIKit Additions. For instance, index paths are used to designate user selections using the tableView:didSelectRowAtIndexPath: delegate method.

Google searches are probably your best bet, such as index path from row and section.

I hope we’ll see improvements on this in the future.

On Frameworks

A coworker sent me a link to this posting on the state of libraries on iOS. I sent back a quick reply, with the intent to write a blog post on the subject later.

I’ve since decided that this replay says almost everything I wanted to say, so I decided to just edit it a bit.

I’m pretty torn on this whole subject. Dynamic libraries can lead to inefficiencies in storage space if a developer isn’t careful, and I’ve no reason to believe iOS developers are careful. Include Google Toolbox for one function? Sure, why not? Apple’s always balanced the experience towards users, and even when it makes my life more difficult I like that.

The static library approach isn’t a good approach either. Sometimes I’ve had less than complete debugging info, and been missing information in crash logs. And its code stripping isn’t as good as it should be, either. The final straw for me was when HockeyApp’s crash reporter stopped producing useful stack crawls. I spent a few hours trying to fix this, even got a little help (HockeyApp is excellent, and their support is outstanding) but quickly realized I was spending too much time on a problem with a very pragmatic fix.

(What was the problem? I’m not sure. It seemed to be related to an Xcode update, but after a few hours I grew less interested in solving it and more interested in shipping what was a critical update to my app.)

Cocoapods tries to solve this, but I think it does so the wrong way. You still have the same problems, it’s just a little less trouble to keep it going. It’s probably better than nothing, but it wasn’t the approach I wanted to take.

What I’ve been doing recently is including the sources in groups within my project. This is harder for me, but better for the compiler/linker (it has a better sense of everything I’m trying to do), debugger (all the symbol information is built as part of the main project) and the end user (who gets a better stripped executable).

What we need is not static libraries or dynamic libraries or frameworks. We need something better, more tuned to iOS.

There’s a few options:

Until Apple has a good solution to this, I’m going to continue to avoid static libraries (except for very specific libraries, like HockeyApp) and put the sources in a group.

I really hope Apple comes up with a good solution soon, though. This is indeed painful. I think Landon’s radar is entirely positive; Apple isn’t too likely to just do the easy thing. They’ll think about, evaluate possibilities, then do something that we can hopefully live with. Hopefully something better than any of us anticipated.

Change back button title

When you’re using a navigation controller, the title of the back button on a particular view controller is pulled from the view it leads to.

Although this can be initially confusing, this actually makes a lot of sense. If two different view controllers (say, Circles and Squares) might push the same view controller (Details), shouldn’t the text in the top left of that view controller depend on which controller pushed it (Circles or Squares)?

Think of it this way:

thisViewController.visibleBackButtonTitle =
    previousViewController.navigationItem.backBarButtonItem.title
    ?: previousViewController.navigationItem.title
    ?: previousViewController.title;

This won’t compile; visibleBackButtonTitle doesn’t actually exist.

There are a few reasons to show custom text on the back button.

More generic

The most obvious reason is that your title relies on user input that may be too long, and you want something simpler and more generic.

This might be the case if the user taps an event. The title in the navigation controller should be the event’s title, but you might want to use something shorter like Event on the back button.

This case is simple: select the parent view controller and give it a custom Back Button value.

More specific

You might also have the opposite problem, where your context is not visible in the navigation title because it’s obvious from something else in the view controller. But you still want to leave that more specific title as a breadcrumb for the user.

An example of this might be an address without a label. The first line of the address doesn’t make sense as a navigation title, as it duplicates the view. But if you tap deeper than that, you might want to include something about the address, such as the house number of the position in the list.

This is a little more complicated. You’ll need to do this at runtime: Set the title of the backBarButtonItem for the navigation item of the view controller the button leads to. That will prevent the navigation item’s title from being used.

But there’s a wrinkle. Let’s assume you want to set the title dynamically. You might have something like this in your parent view controller, which is called by your viewWillAppear and whatever triggers updates of the view controller:

- (void)loadData {
    // lots of other stuff
    self.navigationItem.backBarButtonItem.title = @"dynamic title";
}

This probably isn’t going to work for you as is.

You’ll find lots of crazy solutions to this; manipulating the backBarButtonItem by hand and restoring it after for instance. (There are much, much crazier solutions.)

The key is understanding why it’s not working: backBarButtonItem is nil.

To make it not nil, you have two options:

  1. Select the navigation item in the storyboard editor and give Back Button a value. This value will cause the backBarButtonItem to be automatically created with the view controller is instantiated.
  2. Create the backBarButtonItem at runtime in your viewDidLoad or awakeFromNib (suggested by @calebd). I don’t recommend doing this in code; you’re already using a storyboard or nib, so it’s much simpler to just put a Back Button value into your view controller.

Once it’s created — whether in code or by storyboard — its title can be changed. You can change backBarButtonItem’s title at any time when the parent is displayed, as often as you like: it’ll only be shown when you push something else on top of it.

Summary

If the text is always the same:

  1. Select the parent view controller’s Navigation Item in the editor.
  2. Put the text into the Back Button value.

If the text is dynamic:

  1. Select the parent view controller’s Navigation Item in the editor.
  2. Put some text into the Back Button value.
  3. Set the title in the parent view controller, when its contents change: self.navigationItem.backBarButtonItem.title = dynamicText;

Empathy in pricing

I don’t know much about pricing products as a developer. While I’m a programmer, I don’t have apps of my own yet. So take this with a grain of salt, go ahead and flame me, etc. These are just my feelings.

But nevertheless, I wanted to share how I feel about pricing as a user of software.

I’m going to start with something bold, then work my way back to it: Never let me perceive your product’s pricing is unstable and currently at a high.

Read the rest of this entry

Check your thread

When you start using Grand Central Dispatch or NSOperation, you’ll want to perform some actions on the main thread and some intentionally off the main thread.

This is a simple and obvious technique, but it took me a while to adopt it: You can do by asserting with NSAssert or NSCAssert for [NSThread isMainThread], just as you would assert any other condition.

Read the rest of this entry

Presenting BlockAssert

Assertions are a great tool. As an Objective-C programmer, I use NSAssert and NSCAssert liberally.

For various reasons, you sometimes can’t use NSAssert in a block easily. I’m going to explain why and describe a new macro, BlockAssert, which solves this.

Read the rest of this entry