XCConfig: Swift Macros

In the Age of C, there were these things called Macros. They began with a hash sign (e.g. #) and used the word “define”.

Macros were meant to be an alias that would replace wordy things. For simple string constants and numbers, Macros were very useful, but there were some who tried making complex macros which would then result in hard to read and hard to debug code.

It gave macros a bad rap.

So bad that swift decided to exclude them. It sent the mighy Macros into hiding. Swift does not allow Macros. So if you were looking for a way to store global constants in your app you would have to use a global “let”

let global_string: String = "Winter is coming"

This is fine.

You can totally dedicate an entire file to swift global string constants in this way. However, by doing this, you must recompile the swift code.

An alternative is to use XCConfig and create a preprocessor macro that way you will not need to recompile any swift code. Instructions on how do do this below the dots.


XCCONFIGs are good for 3 things:

  1. Abstracting build settings
  2. Abstracting Info.plist settings
  3. A Home for Global Constants.
  4. A Home for Global Flags

Steps for the first 2 can be found with a simple google search, therefore I will spend this blog entry on the last two.

Global Constants

  1. Create a configurations file and add it to your project

2. Tell your project to use the configuration file

Click on your project file> under “PROJECT” click your project file> under “Configurations” choose your config file.

My XCConfig file is named Common.xcconfig so I have to tell my project and my target to reference it.

3. Open your XCConfig file and write something like this…

PARSE_APPLICATION_ID = “r7EqA8YMRx0ew2OaE2ebKZnErKWdJgmR1hBtQBAQ”
GLOBAL_CONSTANTS = PARSE_APPLICATION_ID="$(PARSE_APPLICATION_ID)"

4. project> build settings> Preprocessor Macros

This is where we are suppose to put our Macro, but the placement of this makes it like a hunt for lost treasure. If you click on “Preprocessor Macros” under quick help you will see the name of the key “GCC_PREPROCESSOR_DEFINITIONS”

We need to add our macros to this like so…

PARSE_APPLICATION_ID = “r7EqA8YMRx0ew2OaE2ebKZnErKWdJgmR1hBtQBAQ”
GLOBAL_CONSTANTS = PARSE_APPLICATION_ID="$(PARSE_APPLICATION_ID)"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(GLOBAL_CONSTANTS)

$(inherited) means that any previous value that GCC_PREPROCESSOR_DEFINITIONS had would not be overwritten by our new addition.

Save the xcconfig file and if you set this up correctly then you should see something like this…

The Macro that we just added was extracted from our xcconfig file and placed here automatically.

Lets add a second one for fun…

PARSE_APPLICATION_ID = “r7EqA8YMRx0ew2OaE2ebKZnErKWdJgmR1hBtQBAQ”
SECOND_ONE = "SECOND ONE"
GLOBAL_CONSTANTS = SECOND_ONE="$(SECOND_ONE)" PARSE_APPLICATION_ID="$(PARSE_APPLICATION_ID)"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(GLOBAL_CONSTANTS)

5. Great… so can I use this macro now? Not quite. Remember how I said that Macros are a C thing? well now you need to add a bridging header that way Swift can access the macro.

Create a header file and name it “TargetName-Bridging-Header”.

Replace “TargetName” with the name of your Target. My target is called “RateMySnack”

If XCode fails to figure out that this file is the bridging header then you may need to manually tell xcode where it is….

Now run your app. Now you should have the power of MACROS!

  • Note: I have found that Macros can either be String, Int, or Float. I have not yet discovered how I can do more beyond that.
SIMPLE_MAC = (2*3) // Doesn't work
SIMPLE_MAC1 = 6 // works
SIMPLE_MAC2 = 6.0 // works
SIMPLE_MAC3 = "works!"

Flags

Add this to your xcconfig file

COOL_FLAG = -D’COOL_FLAG’
SECOND_FLAG = -D’SECOND_FLAG’
OTHER_SWIFT_FLAGS = $(COOL_FLAG) $(SECOND_FLAG)

This adds “COOL_FLAG” and “SECOND_FLAG” to the build setting, “OTHER_SWIFT_FLAGS”

Flags do not have a value. They a binary. Either they exist or they don’t which means if that if I want to set the COOL_FLAG to false I need to comment it out.

//COOL_FLAG = -D’COOL_FLAG’
SECOND_FLAG = -D’SECOND_FLAG’
OTHER_SWIFT_FLAGS = $(COOL_FLAG) $(SECOND_FLAG)

Check the build settings to see if you did it right. if it is right it will show up, otherwise it will be blank. To fix it just enter $(inherited)

Why would you want to use FLAGs? They allow you to include/exclude chunks of code from being compiled

#if COOL_FLAG
print(“WE Are Cool”)
#endif
#if SECOND_FLAG
print(“Second the best”)
#endif

For more information about XCCONFIG click here

Update (4–3–2016)

After upgrading to Xcode 7.3, these macros no longer seem to work. They appear in the build settings as “user defined” but it seems like they are no longer accessible by the app. If there is a way, please let me know in the comments.

Show your support

Clapping shows how much you appreciated Derrick Ho’s story.