Compiler directives in Swift

· 3 min read
Compiler directives in Swift

This post is going to list some of the most frequently used compiler directives in Swift, compatible in Swift 3/4/5. Someone might call this preprocessor directives, but they are the same concept. In this post, we list few commonly used directives with detailed examples:

Updated in 2022

#sourceLocation : This directive is used to provide the location of the source file, in the form of line and column numbers, to the compiler. This can be used to improve the debugging experience.

#sourceLocation(file: "file.swift", line: 2)
print("This is a line of code")

Environment Checking

This is the highly recommended and useful Compiler directives which used quite often while coding. For example, you can tell the program to use different values for different environments:

var url = ""
#if DEBUG
// use localhost while under debug mode
url = "https://localhost"
#else
// use production url while under release
url = "production url"
#endif

// You can also negate the flag
#if !RELEASE
// use localhost while under debug mode
url = "https://localhost"
#else
// use production url while under release
url = "production url"
#endif

With the above code, you can build and compile an IPA file with a different URL based on the environment, so you don't have to build and compile two versions (Debug and Release) separately.

Platform Detection

When you want to cross apple platform:

#if os(OSX)
// compiles for OS X
#elseif os(iOS)
// compiles for iOS
#elseif os(tvOS)
// compiles for TV OS
#elseif os(watchOS)
// compiles for Apple watch
#endif

With the above code, it will automatically use the right code block if it is running on a different platform.

Warnings and Errors

It is quite strange that developer add warning and errors to the project themselves. But such tricks might be useful in some cases, such as adding a warning to remind yourself to improve some part of the code later on.

#warning("Needs improve on the performance")
#error("Needs remove the duplicated code")

A detailed example:

import Foundation

class MyClass {
    // Some code here
    #warning("This is a warning message")
    var myVariable: Int = 0
    // Some more code here
}

In this example, the #warning directive generates a warning message in Xcode when the code is compiled, reminding the developer that this variable is not being used. Similarly Xcode will give out error and stop the compile when reach #error():

Warning and error message

Language version Check

We can also check the current Swift version. As we all know Swift language is still under development, there will be new syntax coming in new version. Therefore we can use this preprocessor flag to check whether it is supported:

// Check the Swift version
#if swift(<5)
#endif

Running Device Check

This is quite useful when we want to check if the current code is running on a specific device. For example, we can pass a certain mock data while using simulator:

// Check environments like Simulator or Catalyst
#if targetEnvironment(simulator)
#endif

Module check

// Check if a module presents
#if canImport(UIKit)
#endif

There are also some other non directives but used quite often in the project, for example, It is quite common to see this in the project if it is trying support old SDK versions. such as iOS 10+.

iOS Version Check

if #available(iOS 10.0, *) {
}

@available(iOS 10, macOS 10.15, *)
func newMethod() {
	//  this method will be only available when iOS >= 10, macOS >= 10.15
}

#function, #line and #file

Those 3 flag or directives are quite useful while debugging, considering the following example:

import UIKit

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    print(#file, #line, #function)
  }
}

Which will print out the following:

/yourpath/Swift Custom Compiler Directives/ViewController.swift 
7 
viewDidLoad()