Approach to iOS

Working for a digital agency in Brisbane specialising in .NET and Objective-C/iOS, I was recently asked about my approach to programming in Objective-C when working in a team/agency environment. So I thought it would be useful to write some of this discussion down for future reference :-)

Patterns – Why?

These patterns are in place to facilitate teamwork and make sure all team members follow a similar “agreed-upon” pattern of development, so if a project scales, the architecture can also scale accordingly.

From this point on, it’s important to think of all code you write as team code (even if you’re asked to put together a quick prototype). Put yourself into another dev’s position and ask:

  • Can they pick up my code and run with it?
  • Have you followed our team’s architecture?
  • Can you abstract any of your code to make methods simpler to read?
  • Have you reduced unnecessary nesting?
  • Do you need to add more comments?
  • Do you need to structure/format your code to make it easier to read (for EVERYONE, not just you).

In a studio environment, you WILL get swapped out of a project and another dev needs to be able to just pick up your work and follow the same pattern.

Quoting

Check all ego at the door. You need to start quoting for teamwork and production-ready mobile code. Just because you can do something fast, consider your team members who may get swapped into your project. If it would normally take you four hours, add extra time to consider writing team friendly code (allow time for you to review your own code for performance bottlenecks, adding comments to clearly explain your work etc.).

iPhone projects, for some reason, seem to keep getting clients who have unfinished web-services. The other problem is sometimes clients will require a fixed-quote up front.

So two big issues right? We’re coding against a moving target and we need to understand every intricate part of a project before it begins (so we cover ourselves from a quoting perspective). If ANY elements of your project are a moving target, you need to add a LOT of time for this. It bites us in the ass every time. The last thing you want is that conversation with the PM saying “oh, but they changed this thing and we had to change this, that and this other thing”. Justify your time by saying these things up front, make PMs aware from the very beginning so that change requests can be negotiated early if necessary. Write down all reasons why you’ve assigned so much time. If it’s too much time, the PM (and you) can look at ways to approach the problem differently so that it will be more in-line with a client’s budget.

The point is, it’s our responsibility to give a client a realistic quote of what they’re asking for. If it’s going to take too long, they can consider alternatives. Think everything through from start to finish before quoting, or you WILL get bitten, and someone higher-up will notice.

If PMs are pestering you to get quotes done faster, tell your team lead that you need more time to quote properly. Escalate where necessary, but yea, any quotes you do are VERY important. Never just say a number, give a best and worst case so PMs have something to go on.

Mogen Data Model

We use Mogenerator (https://github.com/rentzsch/mogenerator) for managing an in-memory data model in our projects. Think of this purely as a flexible class generator, nothing more. It piggy-backs off XCode’s core data model, and spits out class files that we use for managing our own in-memory data model.

Have a read of the github page, but basically once mogen is setup, we have a “Build Data Model” scheme in each project. Your project will also include a /Model/CoreDataModel/generateDataModel.sh file. This file contains all the magic. It tells mogen all the parameters it needs, including any custom class templates you have setup (templates are bundled with each project via a git submodule). In your project’s targets, click the “Build Data Model” target and you’ll see the hook to the generateDataModel.sh file.

Mogen hooks into your xcode data model and spits out machine and human class files. The machine files are your base classes. These get overwritten every time you build your data model. The human files get built once when the entity is first created. This allows you to do any override functionality in the human classes, without fear of your changes being blown away.

Eg.
In your xcode data model, you make a User entity, then build your data model. Mogen will create [project]/Model/ModelObjects/Machine/User.h(+.m) and ../Human/User.h(+.m) class files. You drag these into your project and hey presto, insta-classes (hence the concept of thinking of this as a class generator).

So that’s mogen. You’re best to play around with our SPBoilerplate project if you need to get your head around mogen (or any other concepts).

TSBoilerplate

Massive shout out to Tim here. This is our boilerplate project, which essentially gets cloned when we make new projects. ALL NEW PROJECTS share this base architecture, in terms of MVC, mogen data model and our commands.

A lot of this stemmed out of us getting half way through a project, then things were suddenly a spaghetti-mess and we’d be in over our heads. The reason was, everyone had their own ideas of how they’d code something. There was no central architecture, perhaps only in the lead dev’s mind, who might not be a good communicator = recipe for disaster.

When we’ve got 4+ devs on the same code-base and they’re not sharing knowledge on architecture, it quickly becomes unmanageable. With that in mind, we use Git and Skype to communicate PER PROJECT.

Skype and Git hooks

We use Git …from command line. Yes, there are GUIs for Git that are quite good (we have licenses for SourceTree for example) BUT the reason we use GIt from command line is for custom Skype hooks to facilitate better project communication. So get used to command line please. Ask us questions. We will be able to give you a quick demo on all the basics.

For every project that starts, we create a Skype group conversation. That conversation has a skypeID. We use this ID along with a custom command-line script to push changes to our Git repositories. When the push is successful, it will post the commit-messages to the group Skype conversation. Everyone on that project now knows that you’ve committed/changed something in that project and that they need to update and merge with your changes.

Having a group conversation also allows us to retain a history of all questions/answers for a project. So if you have a question, ASK in the project’s skype conversation. Now if anyone else has that question in the future (or you need to reference it months after the fact), you’ve all got it in the skype history for that project.

Accessor Pattern

We use accessor classes in our projects to facilitate team work and ensure projects do not become unmanageable/spaghetti.

Eg. If you have a data model (say something with 40+ tables), you make a rule for developers that if you need to “access” information, you make an Accessor class to handle those requests (you can create multiple Accessor classes to group various information. Eg. If you have various user tables, you’d make a UserAccessor class), but the point is you make sure everyone uses that same pattern, rather than hanging methods on various classes whereever a developer feels like.

This centralises the accessor logic in one place, so all devs know where to look if they want to look for anything “user” related. Rather than devs having to look through all user-related classes to see what methods are hanging off each user-related class.

If your data model is very small and it makes no sense to make a “User” specific accessor class, then make a generic ModelAccessor class to do any accessor logic for your app. This makes it very easy for anyone coming onto your project to look see what information is being “accessed” in the project, and most importantly, what methods they have available that you’ve already written.

It’s one of the biggest gotchas I’ve found for project overruns (both .NET and iOS) …because we had some devs putting accessor methods (Eg. GetAccountDetails) off the User class, and some people putting accessor methods off the UserAccount class, and before you know it no-one knows which way is up. Having a central UserAccessor class solves this problem …AS LONG AS EVERYONE FOLLOWS THE PATTERN :-)

Eg. This is taken from a sample ModelAccessor class.

@interface ModelAccessor : NSObject

/**
 Searches for a blog category with the given name in our model.
 */
+ (BlogCategory*)blogCategoryWithName:(NSString*)name;

// ... etc. You get the idea.

@end

@implementation ModelAccessor

+ (BlogCategory*)blogCategoryWithName:(NSString*)name
{
    NSPredicate *filter = [NSPredicate predicateWithFormat:@"%K == %@", kModelPropertyBlogCategoryName, name];
    NSArray *arr = [[Model sharedModel].blogCategories filteredArrayUsingPredicate:filter];
    return (BlogCategory*)[arr lastObject];
}

@end

Builder Pattern

We use builders to help serialise data from json requests to our data model and vice-versa. So if we have a service that’s getting a bunch of flight information from Brisbane Airport, we’d have a FlightBuilder class to facilitate all builder logic.

It’s important to learn to separate your builder logic (eg. when you get a response from a json web-service, you need to build that response into an object that you can put in your data model). Junior devs tend to mix builder code in with the rest of their code, whereas we made an architecture that clearly separated code into builder classes, accessor classes and command classes …it’s just sticking to MVC really and keeping all your code clean.

Please keep in mind, a builder’s job is to build, nothing else. It does NOT update the model, it does not do funky lookups on some other area of the app. It builds.

Eg: This is taken from a JourneySectionBuilder class to help build “Journey Sections”.

@interface JourneySectionBuilder : BaseBuilder

/**
 Gets journey sections from the given json response.
 */
+ (NSArray*)journeySectionsFromJSON:(id)json;

/**
 Gets a journey section from the given json response.
 */
+ (JourneySection*)journeySectionFromJSON:(id)json;

// ... etc. You get the idea.

@end


@implementation JourneySectionBuilder

+ (JourneySection*)journeySectionFromJSON:(id)json
{
    if (![json isKindOfClass:[NSDictionary class]]) {
        return nil;
    }
    
    NSDictionary *jsonResponse = [self validDictionaryResponse:json];
    JourneySection *journeySection = [JourneySection new];
    
    ASSIGN_NOT_EQUAL_NOT_NIL(journeySection.code, [jsonResponse stringForKey:@"serviceId"]);
    ASSIGN_NOT_EQUAL_NOT_NIL(journeySection.name, [jsonResponse stringForKey:@"serviceName"]);
    // ... etc. You get the idea.
    
    return journeySection;
}

+ (NSArray*)journeySectionsFromJSON:(id)json
{
    NSDictionary *jsonResponse = [self validDictionaryResponse:json];
    NSArray *servicesJson = [jsonResponse arrayForKey:@"services"];
    
    NSMutableArray *objects = [NSMutableArray array];
   
    NSInteger recordCount = 0;
    for (NSDictionary *objectJson in servicesJson) {
        JourneySection *object = [self journeySectionFromJSON:objectJson];
        if (object == nil || [objects containsObject:object]) continue;
        object.sortIndexValue = recordCount;
        [objects addObject:object];
        recordCount++;
    }
    
    return objects;
}

@end

Command Pattern

I’ll let you explore our CommandRunner classes in TSBoilerPlate, but keep in mind that we use blocks in our command pattern so you can fire off an async command to web-services, then clearly see what’s going to be executed when that command gets a response. With this in mind, nesting is important. Don’t go overboard and nest the hell out of commands. Nesting should be minimal to keep your code/logic easy to follow. Always try and simplify your code. Avoid complexity wherever possible (this not only applies to commands, but everything you write) :-)

Storyboards and Re-usable Xibs

We use storyboards for our projects (the new projects anyway), but you will have need for writing re-usable xibs for cells/views. We have helper classes in place to facilitate loading an iPhone/iPad xib file from code.

For example, if we need to load a xib file from code, we use this method:

SomeViewWeMade *someView = [[SomeViewWeMade alloc] initWithPlatformNib];

What this does is load the appropriate xib file depending on whether your project is iPhone or iPad (or a universal app). As long as you name your xib files correctly (with “iPhone” and “iPad” postfixes accordingly, then this method will do the work for you. We do not want to see if/else statements in code switching between iPhone and iPad views.

For the above example, our project would have a SomeViewWeMade.h/.m, SomeViewWeMadeiPhone.xib and SomeVIewWeMadeiPad.xib file in the project under their respective folders.

Comments

You’ll notice we use doxygen-style comments like this /** … */ for all interface method declarations. We do this because we have a git submodule AppleDoc which we can use to generate documentation for our projects. AppleDoc will search for those comments and spit out an API for us if required.

Comments are mainly for your fellow developers though. So get those useful thoughts out of your head and into the comments please. No one reads it except developers, so you can be relaxed on the language used. It’s a dev-to-dev communication device …use it!

AppState

You’ll notice an AppState class in our projects that sits along-side the Model. This came about over 6 months ago when we had arguments in the team about people hanging properties off view controllers and violating MVC at times, because they were hanging strong references of model data directly on view controllers (so in essence, the controller OWNS that model data …not good).

Eg. You might have a ProductDetailViewController which you use to view a given Product. People were making a Product property and hanging it directly off the controller. This is not uncommon, hell, even Apple do this in their examples. But we do not :-)

The reason can be stated simply that this violates MVC; controllers should not own model data. But a more practical explanation comes from understanding that using AppState instead will simplify your logic and make your code easier to understand and safer from a memory management point of view.

Using the previous Product example, our model would contain a collection of products. Therefore, we only need a weak reference to this product, so as not to “up” the retain count unnecessarily. So we ‘could’ hang a weak property off the view controller, but what if the navigation bar, or some footer view also needed to know what Product was currently being viewed? If you hang properties off controllers, then all those views have to either have their OWN Product property assigned, or they need to messily reference the ProductViewController’s property. It quickly becomes a nightmare.

Instead, if you make an AppState property called viewingProduct and set this whenever the user is viewing a product, ALL classes in your project can easily determine what product is currently being viewed by going through the AppState.viewingProduct property. You set this up as a weak reference, so the AppState class will not cause any weird retain cycles and everyone’s happy.

Actual Standards / Basic Checklist

Take 10 minutes before each commit and please make sure of the following:
* all interfaces are defined for all methods …xcode does not require this anymore since iOS4?, but we still need to do this from a code-management point-of-view.
* all methods are commented and those comments are above the interface declaration, not the implementation (for private methods, declare a private interface up the top of your implementation file …essentially an unnamed category).
* alphabetise your methods / properties to make it easy for ppl to find things at a glance (Google do this internally and don’t accept your commit unless you’ve met this criteria, we need to try to do this as well, for larger projects it is necessary).
* comment unclear properties (commenting properties might seem a bit overkill, but for large controllers it can get confusing when you have 20+ properties).
* analyse your code for anything that can clearly be refactored.
* do your methods look like the other methods in a class? If not, ask why and keep them all consistent.
* keep consistent patterns and spacing, work from known examples, strive to make your code easy-on-the-eye (like Ed) …any questions raise it in team chat.
* name enum options correctly! :-P …if you make an enum called SyncEvent, the options should all be prefixed with “SyncEvent”, so you’d end up with SyncEventStart, SyncEventInProgress, SyncEventComplete …this makes it super easy for any programmers to start writing “SyncEvent…” in code and see all the options, without having to lookup the enum and determine its options.

We follow patterns to facilitate teamwork. Patterns can be changed if/when all team members agree that a change is necessary, so if you have a problem, raise it and don’t be shy (but be prepared to fight for your case and be willing to accept the situation if the group disagrees with you) …people passionate about code is what got our architecture to where it is today :-)

That’s all

That’s all I can think of atm, my fingers are tired :-)

Happy coding.