In the previous article, I took a look at building “traditional” enums from Swift into ObjC. This time, we’ll take a look at how we might build the “associative value” enum.
Feature Set
Swift also allows you to create “enum” values that are able to store various pieces of data stored alongside that enum value. The requirements are essentially this:
- Store associated values of any given type
- The value types can be different for each member of the enumeration
Swift Examples
In Swift, one such enum might look like this:
enum Barcode {
case UPCA(Int, Int, Int, Int)
case QRCode(String)
}
swift
You create one like this:
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
And you use it like this:
switch productBarcode {
case .UPCA(let numberSystem, let manufacturer, let product, let check):
println("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode):
println("QR code: \(productCode).")
}
swift
Essentially what you are creating is a set of classes that hold data grouped under a Barcode
namespace. We can do something similar in ObjC.
ObjC Usage
Let’s start with how we use it. First, how we declare it:
Barcode *productBarcode = [Barcode UPCA:8 :85909 :51226 :3];
Pretty similar to Swift, not too bad.
And how about that switch-statement? Well, we can do it with an if-statement instead:
if ([productBarcode isKindOfClass:[UPCA class]]) {
NSLog(@"UPC-A: %ld, %ld, %ld, %ld", [(UPCA *)productBarcode part1],
[(UPCA *)productBarcode part2],
[(UPCA *)productBarcode part3],
[(UPCA *)productBarcode part4]);
}
else {
NSLog(@"QR code: %@", [(QRCode *)productBarcode part1]);
}
objc
Ok, the ObjC version is starting to look ugly again because of a syntax problem; we cannot pull out the components and nicely as we can in Swift… but we’ll address that later too.
ObjC Implementation
Ok… so how do we implement this in ObjC? Well, remember, this is really just a special type that is getting returned depending on which “enum” value you want.
@class UPCA;
@class QRCode;
@interface Barcode : NSObject
+ (UPCA *)UPCA:(NSInteger)part1 :(NSInteger)part2 :(NSInteger)part3 :(NSInteger)part4;
+ (QRCode *)QRCode:(NSString*)barcode;
@end
@interface UPCA : Barcode
@property (assign) NSInteger part1;
@property (assign) NSInteger part2;
@property (assign) NSInteger part3;
@property (assign) NSInteger part4;
@end
@interface QRCode : Barcode
@property (copy) NSString *part1;
@end
@interface UPCA (Private)
- (instancetype)initWith:(NSInteger)part1 :(NSInteger)part2 :(NSInteger)part3 :(NSInteger)part4;
@end
@interface QRCode (Private)
- (instancetype)initWith:(NSString *)part1;
@end
@implementation Barcode
+ (UPCA *)UPCA:(NSInteger)part1 :(NSInteger)part2 :(NSInteger)part3 :(NSInteger)part4
{
return [[UPCA alloc] initWith:part1 :part2 :part3 :part4];
}
+ (QRCode *)QRCode:(NSString*)barcode
{
return [[QRCode alloc] initWith:barcode];
}
@end
@implementation UPCA
- (instancetype)initWith:(NSInteger)part1 :(NSInteger)part2 :(NSInteger)part3 :(NSInteger)part4
{
if (self = [super init]) {
self.part1 = part1;
self.part2 = part2;
self.part3 = part3;
self.part4 = part4;
}
return self;
}
@end
@implementation QRCode
- (instancetype)initWith:(NSString *)part1
{
if (self = [super init]) {
self.part1 = part1;
}
return self;
}
@end
objc
WOW! That is a ton of boiler-plate code. But we are there, we have the same functionality that we have in Swift. Would you I want to write this? Eh… no. But, the point here is just to see how we might implement these features from Swift in ObjC directly. There are of course many optimizations we can do and generalizations to make; this is only one naïve implementation of “associated values”.
More next time.