Swift Byte #2: A "Swifty" Way to Handle Preprocessor Macros
A simple approach to make handling preprocessor macros a tiny bit easier
Today we will be talking about handling preprocessor macros. As the name implies, the value of these preprocessor macros will only be set or run before your code is compiled. Meaning, everything in the else block will not be included in your app binary.
#if DEBUG
print("do something in debug mode") # will be included
#else
print("do something else") #will not be included
#endifSwift preprocessor macros are pretty helpful when you want to compile and run the code for specific build configurations. For example, the code above will only be run when your app is in debug mode (attached to a debugger).
It’s a useful feature. Some use it to exclude code that might cause Apple to reject their app during app submission. Some use it to configure their white-label apps. Most used it so that they can have different build configurations for different environments.
However, having these macros littered around in your codebase might make debugging harder. Plus, it’s not exactly Swifty, right?
One thing I like to do to solve this issue is to wrap all preprocessor macros checks in a single Environment enum. For example:
enum Environment {
case beta
case staging
case production
}
extension Environment {
/// To check which dev environment app is currently running on
static var current: Environment {
#if PRODUCTION
return production
#elseif STAGING
return staging
#elseif BETA
return beta
#endif
}
/// To check if app is in debug mode or not
static var isDebug: Bool {
#if DEBUG
return true
#else
return false
#endif
}
...
}With this setup, we will be able to check for the macros condition using Swift language features. For example:
You can use
switchstatement to return the correct API’s hostname depending on your current build configurations.
var hostName: String{
switch Environment.current {
case .production:
return "example.com"
case .staging:
return "staging.example.com"
....
}
}Or you can use
guardstatement to check if you are in debug mode so that you can avoid logging sensitive info in production.
func log(items: Any...) {
guard Environment.isDebug else { return }
print(items) // may contain sensitive data
}Personally, I like this approach more just because I can make use of Xcode auto-completions feature.
That’s all for today. I hope these simple tips help to make your iOS development experience better.
What do you think? Have a better or nicer way to handle this? Let me know in the comments section below.
Happy Friday!


