Converting Complex Objective-C Macros to Swift Functions

The question of how to convert #define macros from Objective-C to Swift is explained fairly simply in the Apple developer documentation on the subject. For simple macros, it’s a matter of rewriting them as global constants. In fact, if you’re using the hybrid Objective-C — Swift approach to writing your app, Swift sees those simple macros and automatically makes them available to your Swift code. I also gave some tips on the alternative to Objective-C macros a while back.

Where we run into trouble is when we need to port complex Objective-C macros to Swift. According to the same documentation from Apple,

Complex macros are used in C and Objective-C but have no counterpart in Swift.

Yikes!

Thankfully there is a silver lining after that scary first sentence:

Complex Macros Explanation

In Swift, you can use functions and generics to achieve the same results without any compromises.

That makes sense, actually! Complex Objective-C macros tend to look a lot like functions, so the transition to Swift was straightforward in a case I ran across recently.

Two Examples:

A simple example

What could we do in Swift to convert an Objective-C macro that looks something like this?

One thing we could do is write a function that produces the same thing:

A little more complicated

An example situation that came to me on Twitter took the form of converting a macro that was a little more complicated than the simple example just presented. The input to the complex macro was a color, represented as a hexadecimal value, along with an alpha, represented as a float. The output? A UIColor instance based on some bitwise manipulations to that hex value.

I’ve created a GitHub example if you’d like to play around with everything. The relevant code is reproduced below…

The macro form looked like this:

Rewriting it as a Swift function:

The main thing to keep in mind is that the output of the macro/function is the focus. The internals could change to better-adapt to Swift’s features if you desire. If the macro was ugly inside, make it nice in Swift!

Where should the function go?

  • For organization’s sake, you could create a new .swift file and place the function inside it at the global level. This would provide the most convenient transition for your Objective-C to Swift conversion, because #defines were available wherever you imported the Objective-C header file.
  • Alternatively, you could encapsulate the function in a class/struct/enum.

Wrapping up

With the power of Swift functions and the ability to even declare and use them globally, converting complex macros to a better Swift alternative is much less daunting than you might expect.

  • smykytyn

    In Objective-C I’ve been using a macro to create shared instances, mostly for convenience, but also to make sure it’s done right in every case.

    #define GCD_SHARED_INSTANCE_FOR_CLASS(classname)

    + (instancetype)shared {

    static dispatch_once_t flag;
    static classname *shared = nil;
    dispatch_once(&flag, ^{ shared = [[self alloc] init]; });
    return shared;
    }

    The Swift shared instance function that seems most straightforward is:

    private let _SomeManagerSharedInstance = SomeManager()

    class SomeManager {
    class var sharedInstance: SomeManager {
    return _SomeManagerSharedInstance
    }
    }

    Not sure how to make a Swift function that can work for multiple classes, like the macro does.

    • Andrew Bancroft

      I’m not really sure either, but if I think of something I’ll definitely let you know. GCD_SHARED_INSTANCE_FOR_CLASS(classname) seems to imply that a Swift function counterpart might involve generics. Not sure what that’d look like though without researching further.

  • chrisbrandow

    I could be completely wrong, but shouldn’t the square example be using generics in order to be as useful as the macro is?

    • Andrew Bancroft

      You have an excellent point! I’ll expand the example to account for that – because you’re right… the macro could take just about any kind of ‘n’ I’d think. Thanks very much for pointing that out!

      • chrisbrandow

        hmm. In my ignorance, having not really used generics, I thought it would be a super simple exercise to write something like:
        func squareNumber(n: T) -> T {
        return n * n
        }

        but… it’s not that simple ( http://natecook.com/blog/2014/08/generic-functions-for-incompatible-types/ )

        the function declaration is nearly that simple, but the operators have to be told what to do with the generic types. This works. It undermines the simplicity, but it should only have to be done once for mathematic Macros, presumably:

        extension Double : NumericType {}
        extension Float : NumericType {}
        extension Int : NumericType {}
        extension Int8 : NumericType {}
        extension Int16 : NumericType {}
        extension Int32 : NumericType {}
        extension Int64 : NumericType {}
        extension UInt : NumericType {}
        extension UInt8 : NumericType {}
        extension UInt16 : NumericType {}
        extension UInt32 : NumericType {}
        extension UInt64 : NumericType {}

        protocol NumericType {
        func +(lhs: Self, rhs: Self) -> Self
        func -(lhs: Self, rhs: Self) -> Self
        func *(lhs: Self, rhs: Self) -> Self
        func /(lhs: Self, rhs: Self) -> Self
        func %(lhs: Self, rhs: Self) -> Self
        init(_ v: Int)
        }

        func squareNumber(n: T) -> T {
        return n * n
        }

        println(“one: (squareNumber(5))”)

  • Pingback: Converting Objective-C Macros To Swift Functions [iOS developer:tips];()

  • soniccat

    It seems there’s no way to convert string concatenation (##) to generate new class name for example to swift.

    • http://www.iangrossberg.com Ian Grossberg

      Yea, great post Andrew, however the examples provided could have easily (and should have been already) Objective-C selectors. One of the strengths of C macros that unfortunately so far has not been matched is the ability to systematically compose composited values into symbols, whether to access something existing or declare something new.

      • Andrew Bancroft

        Great points, Ian. Thanks for sharing the case for using selectors over macros for the types of examples I provided.

        I’ve spent far less time in the C / Objective-C world than many others, so I ended up making very seldom use of macros myself. I’m curious about what you were saying about systematically composing values into symbols…

        Would the situation you’re describing there relate to Apple’s comment about “avoiding typing large amounts of boiler plate code”? This is showing my lack of experience in the area, but I’m curious – if you have a small example, I’d love to see it, just so I’m able to relate to the issue.

  • jcburns

    shouldn’t it be:

    let red = CGFloat(Float(((color>>16) & 0xFF)) / 255.0)
    let green = CGFloat(Float(((color>>8) & 0xFF)) / 255.0)
    let blue = CGFloat(Float(((color>>0) & 0xFF)) / 255.0)

    (instead of 24, 16, 8)?

    I kept messing with it and was getting zero values for the red no matter what I did.

    • Andrew Bancroft

      I’ll double-check it and update the code snippets / GitHub project soon. I appreciate you pointing it out!

    • Andrew Bancroft

      You’re absolutely right, now that I look into it deeper. Thank you a ton for helping improve this post!

      I’ve updated the post’s code and the GitHub example with your changes.

  • Pingback: Michael Tsai - Blog - Swift: No Macros, No Compromises()

  • harsh shrivastava

    Hi Andrew, great post! I am trying to use this SEC_IS_BEING_DEBUGGED RETURN NIL() in swift. Any idea how I can implement this? Thank you!

  • Mike Mayer

    I know that I’m late to the game here… but YIKES… your very first simple example made me recoil in horror.

    #define SQUARE_NUMBER(n) n * n

    This is the classic macro blunder. Imagine the case where you used this to square the sum of two variables:

    double ab2 = SQUARE_NUMBER(a+b);

    The preprocessor will expand this to:

    double ab2 = a+b * a+b;

    This is most likely NOT what you want!
    You example should have read:

    #define SQUARE_NUMBER(n) (n)*(n)

    I know this is off topic, but I have had to bail out plenty of colleagues for similar mistakes (typically more complex than a simple multiplication). It can often take a long time to find the problem… usually ends up dumping the preprocessor output and crawling through that.