I thought it would be an interesting thought experiment if we take a look at features that are present in Swift and see what it would take to build them out in ObjC.
Up on the docket today is the Enum
. Of course, Swift really supports three different types of enums:
- The "traditional" enum — simply a list of potential options
- An "associated value" enum — enums with particular values that can be held but grouped under a context
- The "raw value" enum — an enum that stores a specific value for each of the options.
In this post, we'll be looking at the "traditional" enum.
Feature Set
So the traditional enum is quite basic and needs only to implement the following rules:
- Each value represents a unique option
- There is no intrinsic value to any given option
Optionally, it would be nice to be able to:
- Iterate over all of the options
- Know the number of options available
Implementation There are a couple of options available to us:
- Use the C-style enum that is already supported in ObjC
- Build the enum using
@interface
(e.g. classes)
I'll be going with option #2 as it provides the most flexibility and allows us to be true to all of the rules.
I'll be creating the CompassPoint
enum from the Swift language guide.
enum CompassPoint {
case North
case South
case East
case West
}
swift
In ObjC, we need to have the following:
@interface CompassPoint : NSObject
+ (CompassPoint *)North;
+ (CompassPoint *)South;
+ (CompassPoint *)East;
+ (CompassPoint *)West;
@end
#define RETURN_ENUM_INSTANCE() \
static CompassPoint *instance = nil;\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
instance = [[CompassPoint alloc] init];\
});\
return instance;
@implementation CompassPoint
+ (CompassPoint *)North { RETURN_ENUM_INSTANCE(); }
+ (CompassPoint *)South { RETURN_ENUM_INSTANCE(); }
+ (CompassPoint *)East { RETURN_ENUM_INSTANCE(); }
+ (CompassPoint *)West { RETURN_ENUM_INSTANCE(); }
@end
objc
Well… that's looking a bit ugly, but we've met the initial requirements.
Usage of the enum is almost identical to Swift, with two caveats:
- Unable to use the shorter dot-notation
- Unable to use the switch-statement
Here's what the usage code looks like:
CompassPoint *directionToHead = CompassPoint.South;
if (directionToHead == CompassPoint.North) {
NSLog(@"Lots of planets have a north");
}
else if (directionToHead == CompassPoint.South) {
NSLog(@"Watch out for penguins");
}
else if (directionToHead == CompassPoint.East) {
NSLog(@"Where the sun rises");
}
else if (directionToHead == CompassPoint.West) {
NSLog(@"Where the skies are blue");
}
objc
The nice thing about this ObjC version is that it's quite easy to add some additional functionality, like the ability to iterate over all of the values:
@interface CompassPoint (Iteration)
+ (NSArray *)allValues;
@end
@implementation CompassPoint (Iteration)
+ (NSArray *)allValues
{
static NSArray *values = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
values = @[ CompassPoint.North, CompassPoint.South, CompassPoint.East, CompassPoint.West ];
});
return values;
}
@end
Obviously the syntax for the ObjC sucks, especially in comparison to what we get in Swift. There are lots of different options available for us to make this better in ObjC, but more on that later.