For the past 6 months I’ve been working with a client to bring an existing C++ application to the iPad. This is an application that’s been around for a long time. How old? Some of the code was written when this guy was cool. We needed to initialize a large C++ library from our iOS code. This is a long running object that will be used throughout the application lifecycle. The technique described in Phil Jordan’s excellent Mixing Objective-C, C++ and Objective-C++was a good fit for this.
Now, the class was large. Hundreds of methods large. To keep our sanity, we wanted to split up parts of the interface to make our client code more manageable. Our next challenge was splitting up functionality into seperate classes with the following requirements
- Allows us access to the C++ library
- Could be used from plain Objective-C (not only Objective C++)
- Keep our headers clean (thanks Phil)
- The code using these objects should be Testable
We created a Objective-C++ class with an initializer that accepted a pointer from the C++ library.
//forward declare C++ class struct LegacyCPlusPlusStuff; typedef struct LegacyCPlusPlusStuff LegacyCPlusPlusStuff; @interface LegacyWrapper : NSObject -(instancetype)initWithLegacyCPlusPlusStuff:(LegacyCPlusPlusStuff*)LegacyStuff; @property (readonly) NSString *interestingString; -(void)doSomethingCool; @end
Our implementation looks something like this. You’ll notice there isn’t much there but we wanted to keep these wrapper classes thin.
-(instancetype)initWithLegacyCPlusPlusStuff:(LegacyCPlusPlusStuff*)LegacyStuff { self = [super init]; if(self) { _legacyCPlusPlusStuff = LegacyStuff; } return self; } -(NSString*)interestingString { return [NSString stringWithUTF8String:self.LegacyCPlusPlusStuff->getInterestingString().c_str()]; } -(void)doSomethingCool { self.LegacyCPlusPlusStuff->doSomethingCool(); }
Not bad. We can access the data from the C++ class, but our new class is dependent on a C++ type. This means every class that uses this thing is required to be Objective-C++. Mixing Objective-C and Objective-C++ isn’t the end of the world, but I’d like to keep the worlds seperate. In any case, our headers are certainly not clean.
To solve this we’ll create a protocol with the same interface as our LegacyWrapper class. Protocols are analogous to interfaces for those coming from Java or .NET worlds. They are indispensable for writing testable code with C# or Java, but they don’t seem to get a lot of attention in iOS land. Protocols allow us to separate interface from implementation. This is a good practice no matter what language you’re using. You all are doing this anyway, right?
@protocol LegacyWrapperProtocol @property (readonly) NSString *interestingString; -(void)doSomethingCool; @end
Next we create a method to get access to our new class from the main library wrapper.
@interface LibraryWrapper : NSObject -(id<LegacyWrapperProtocol>) createLegacyWrapper; @end
-(id<LegacyWrapperProtocol>)createLegacyWrapper { return [[LegacyWrapper alloc] initWithLegacyCPlusPlusStuff:self.cPlusPlusObject ]; }
By extracting the protocol from the Objective-C++ class we are able to access our desired functionality from Objective-C code.  functionality is logically grouped, our headers are clean and we can mock the resulting protocol with something like OCMock. We ended up using the pattern often to break out different parts of the application. This is pretty basic stuff but techniques for mixing C++ and Objective-C aren’t that well documented. Hopefully someone will find this useful.