From Objective-C to Swift: Use Complex #define Macros in Swift

Swift is a (not so) new programing language announced by Apple on June 2014 during WWDC of the same year. The announcement was exciting and a lot of us iOS developers read half of the Swift book Apple released for free that same night. We couldn’t wait start playing with the language as soon as we could.

One of the biggest issues with adopting Swift was, and still is, that most developers have to deal with a lot of legacy code that was written in Objective-C and works well enough — so it’s hard to justify rewriting it in Swift. So — usually - when developers start to work on Swift it comes as completely new code added to existing Objective-C apps. For most developers who want to learn and start using Swift the first big hurdle is understanding how to make their existing Objective-C code work with the new Swift code.

Apple predicted this issue and developed Swift to be able to work alongside Objective-C with minimum changes to existing code. Read all about using Swift and Objective-C in the Same Project. This works great. In most cases.

Many Objective-C apps use the #define preprocessor macro. These preprocessor macros tells the compiler to replace the definition of the macro before compilation. My team is using these macros in our Objective-C code to define constants.

Example:

#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define OUR_GREEN_COLOR [UIColor colorWithRed:4/255.0f green:180/255.0f blue:4/255.0f alpha:1.0];
#define MAX_ARRAY_LENGTH 20

And then:

self.tableFrame.size.width = SCREEN_WIDTH;
self.view.backgroundColor = OUR_GREEN_COLOR;
self.somethingElse = MAX_ARRAY_LENGTH

Easy as that.

When I started adding new Swift code to our existing app I had no way of accessing some of these #define constants. Swift just wouldn’t recognize them. MAX_ARRAY_LENGTH was available — but SCREEN_WIDTH and OUR_GREEN_COLOR were not.

After some investigation I found the following official documentation by Apple:

Where you typically used the #define directive to define a primitive constant in C and Objective-C, in Swift you use a global constant instead. For example, the constant definition #define FADE_ANIMATION_DURATION 0.35 can be better expressed in Swift with let FADE_ANIMATION_DURATION = 0.35. Because simple constant-like macros map directly to Swift global variables, the compiler automatically imports simple macros defined in C and Objective-C source files.
Complex macros are used in C and Objective-C but have no counterpart in Swift.

First idea I had was to create a new constants Swift file with let counterparts for each of the #define macros. But this will require me to maintain two different files for each constant. I could also 8remove the existing #define macros and replace them with the let constants — but it would be too much work. I had to find a solution that will take little time to implement and will be as low maintenance as possible.

All of our #define macros were in a single Constants.h file. This worked to my advantage. What I ended up doing was creating an Objective-C class method for each of the #define macros — that returned the macro value. All I had to do next was to create a .m file that implemented the class methods, That’s it. #define macro available in Swift.

Example:

.h file:

#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
+ (CGFloat) ScreenWidth;

.m file:

+ (CGFloat) ScreenWidth { return SCREEN_WIDTH; }

And in Swift:

self.tableFrame.size.width = Constants.ScreenWidth;

Since this is a class method you can now use it almost as you would the #define macro. If the #define macro value is changed — it will also be reflected in the class method created to return that macro without any additional code.