A conversation on Twitter last week got me thinking about how I name my classes, particularly the difference between a Manager and a Controller class.
For me, a Manager is the go-to-guy for a particular aspect of functionality or data. For example, a FileManager handles anything related to accessing the file system. A Manager class contains only class methods, or is implemented as a Singleton, as no separate instances are usually required.
[MyFileManagerfileExistsAtPath:myPath];// Class method
A Controller class co-ordinates a process. For example, a PurchaseController controls the series of actions in purchasing a product. A ViewController co-ordinates a series of actions for interacting with a View.
MyPurchaseController*purchaseController=[[MyPurchaseControlleralloc]initWithDelegate:self];[purchaseControllerpurchaseProduct:productId];// Begins purchase process...[purchaseControllercancelPurchase];// Cancels process
Everyone has their own naming preferences, and yours might be different. It’s not really the names themselves that’s important, but that the naming is consistent throughout the application. Remember, it’s all about communicating intent to yourself and others who use your code.
In my last post I discussed using UIAppearance with UISS to style iOS apps. Today I discovered a few more tricks with UIAppearance.
UIKit only exposes a limited set of elements that can be styled with UIAppearance. But what if we want to style something that’s not exposed? Well, it turns out you can define your own UIAppearance proxies by appending a setter method declaration with UI_APPEARANCE_SELECTOR.
Say, for example, I want to use UIAppearance to set rounded corners on a UIView. I can create a setCornerRadius method on a UIView subclass that’s appended with UI_APPEARANCE_SELECTOR:
Now, this is pretty neat, but what if I want to expose cornerRadius on every UIView in the application, not just the subclass? Well, it turns out you can create a category on UIView that defines custom UIAppearance methods:
UIAppearance is a convenient way to define styles for UIKit classes throughout your application. For example, instead of setting the font on every UILabel, you can define it once using a UIAppearance proxy:
Of course this is Objective-C, so the UIAppearance code is verbose and can be quite difficult to read. If you work with a designer who might like to tweak these values, the code can appear daunting. Luckily there’s a quick and simple way to make UIAppearance code easier to read (and write!).
UISS, developed by Robert Wijas, is an iOS library build on top of UIAppearance that provides a convenient way to define styles using JSON.
Let’s look at a more complex example of styles defined in Objective-C using UIAppearance:
The UISS code is not only easier to read, but also more pleasurable to write!
UISS includes logging and a console for troubleshooting style problems. It can even generate the equivalent UIAppearance code from the JSON files, which is handy if you want to see exactly what’s going on.
Styles can even be loaded from a remote server to enable live style updates in your app.
I was able to get up and running with UISS quickly and found it greatly improved the readability of my UIAppearance definitions. UISS is available on GitHub or as a Cocoapod.
iOS 5 introduced Storyboards, which allow you to visually define the flow of an app. The transitions between view controllers in a Storyboard are called “Segues”.
Initially, the benefits of using segues seem clear:
Spend less time writing boring transition code.
Code is cleaner and not littered with transitions.
You get a visual representation of how the app fits together.
However, after using segues for a while I’ve found there are a few downsides:
The visual layout is restricted. Segues always exit from right of a controller and enter from the left. For any moderately complex app, you can end up with a storyboard that resembles spaghetti.
I almost always need to pass data to the controller I’m transitioning to. This requires dropping back to the code to intercept the segue, find the controller, then set its properties.
I often find I end up writing quite lengthy and obscure code to intercept the segue.
Here’s a comparison of transitioning using segues versus a traditional approach. In this code I’m handling tapping a detail accessory on a row in a table view, then transitioning to a view controller.
First, I’ll use a segue:
In theory I should just be able to connect a segue from the detail accessory to the next view controller and I’m done! Not quite… I also need to assign some data on the controller I’m transitioning to. So I need to intercept the segue by implementing the prepareForSegue method, then check the segue identifier to ensure I am handling the correct segue:
So I have the controller and now I need to assign some data to it. But how do I know which row was tapped? There is nothing in UITableView that stores the selected state for an accessory view. So I’ll need an instance variable to store the row index and set this in the tableView:accessoryButtonTappedForRowWithIndexPath: method:
But hang on! The tableView:accessoryButtonTappedForRowWithIndexPath: method gets called afterprepareForSegue, which means my selectedDetailIndexPath variable is not yet set. So now I need to disconnect the automatic segue from the detail accessory and instead create a manual segue between the two controllers.
Next, I need to call performSegueWithIdentifier to manually trigger the segue in the tableView:accessoryButtonTappedForRowWithIndexPath: method
That replaces all the previous code! Feels much simpler, doesn’t it? What I like about this is that the transition code is all in one place, not spread across two methods. Sure, there’s no “visual” representation of the transition, but if the code is clean and clear, it should be easy to determine how one controller transitions to the next.
So should you ditch using segues? Not necessarily. They can definitely save time if you are transitioning between static views. But there may be cases where it’s cleaner and simpler to do a manual transition rather than use a segue.
Segues are a step in the right direction, the less code we have to write the better! Hopefully they’ll continue to be improved in future updates of iOS. But with the current implementation, you often have to write quite obscure code to work-around the limitations.