Naming constants
When talking about constant, there is a distinction worth making:
public string IDs that identify a specific class of objects by being unique such as NSNotification names, NSError domains, etc.
private IDs which are variables aimed at giving a meaningful name to implementation details such as API keys, URLs, values of size, timeouts etc name to a string or number
Especially for the first type of constant, it’s important to choose a meaningful name. It needs to clearly define:
The purpose of the variable: what does it describe?
The scope of the variable: what class does it belong to?
The nature of the variable: is it a notification? Or an error domain?
The standard we came up with is quite verbose but it reflects Apple’s best practices and it’s the following:
[PRE][Class][Name][Type]
Where:
PRE is the class prefix defined for the project.
Class is the class name where this constant is defined.
Name is a meaningful name that describes what the constant defines
Type is a meaningful name that describes what the constant is for. For example: Notification, Error, ErrorDomain, etc.
Public string IDs
Public string IDs have to be exportable: a notification name has to be available outside the defining class in order to allow other objects to subscribe to that notification and so it has to be defined on a .h file. In this scenario, static constants, which are exportable symbols, achieve a better level of encapsulation. Compare these two examples:
#define MYOBJECT_FAILURE_NOTIFICATION_NAME @"myobject.failure.notification.name"
NSString * const AAAMyObjectFailureNotificationName = @"myobject.failure.notification.name";
In the first case the actual value of the notification name, the string myobject.failure.notification.name is publicly available to the client of the class that can decide (in an effort of bad coding practice) to copy and paste the value instead of referencing the #-define.
In the second case, the actual value of the notification is hidden to the client and can be freely changed by the developer of MyObject.
Private IDs
Private IDs are aimed to make source code more readable: by giving a name to an obscure string or number, and referring to that throughout, the code becomes easier to understand. The variable myAPIKey is a lot more clear than a string that looks like: @”25892a73409f8ef78675c765″.
The two ways to define such names is by either #-defines or constant values:
static const NSString *myAPIKey = @"25892a73409f8ef78675c765";
#define myAPIKey @"25892a73409f8ef78675c765"
Unfortunately, #-define are not symbols in the debugger’s symbol table and that can create some struggle when debugging code like the following:
#define MY_API_STRING @"25892a73409f8ef78675c765"; //
NSLog(@"%@", MY_API_STRING);
The macro on the previous NSLog statement expands to:
NSLog(@"%@", @"34643575464532";);
which is a clear syntax error.
Unfortunately, Xcode warning is an unhelpful “expected ‘)'” next to the log statement instead of near the macro definition.
Naming Functions
Another situation in which is tempting to use #-defines is for simple, straightforward functions. Since #-defines are replaces before compile time, these functions don’t incur in any computational overhead and can be useful to abstract bits of algorithms where a normal function call can potentially waste a lot of precious CPU time.
Unfortunately, the pre-processor isn’t very smart and applies a simple literal substitution which can lead to all sort of troubles. Like in the following example:
#define f(x) (x*2)
f(2) // --> 2*2 no problem
f(3-2) // --> 3-2*2 != (3-2)*2 not the expected result!
It’s a well known best practice to add an extra set of parenthesis to define-function arguments like this: #define f(x) ((x)*2) but well.. typos are always lurking around the corner and it can quickly become a situation very hard to debug if not spotted right away.
Moreover, if the function defined is more complicated that a simple arithmetic expression, #define makes it hard to debug cause they can’t be stepped through since, as said before, they’re not represented in the debugger’s symbol list.
Everything is much clearer if we use a real C function and we make it inline if we really need that extra boost in performance:
static inline int f(x)
{
return x*2;
}
Names in categories
A category is a way to add functionality to a class without the need to subclass. This is achieved by the runtime that adds the category methods to the method table for a class when that category is loaded.
It’s clear whether the method is part of the main class or part of a category.
We avoid undefined behaviour due to method clashing.
The two goals are easily achieved by prefixing every category method that extends a class not defined within the project with the usual three-chars prefix, lowercase and followed by an underscore. For example:
@interface UIColor(FWTProjectColors)
+ (UIColor *)fwt_brandTextColor;
+ (UIColor *)fwt_brandBackgroundColor;
@end
Defining colour, image and button constants
On every iOS app with a minimum of branding and design, there are always some recurring visual objects. They usually belong to the following classes:
UIColor: branding colours.
UIImage: images for visual elements.
UIButton & UIBarButtonItem: types of button common to the whole interface.
It’s good practice to abstract these objects into categories that act as factories so that they can be changed across the whole app with ease.
The special case of NSDateFormatter
Discover more from CODE t!ps
Subscribe to get the latest posts sent to your email.