Guides and Sample Code


Using Swift with Cocoa and Objective-C (Swift 3.1)

On This Page

Interacting with C APIs

As part of its interoperability with Objective-C, Swift maintains compatibility with a number of C language types and features. Swift also provides a way of working with common C constructs and patterns, in case your code requires it.

Primitive Types

Swift provides equivalents of C primitive integer types—for example, char, int, float, and double. However, there is no implicit conversion between these types and core Swift integer types, such as Int. Therefore, use these types if your code specifically requires them, but use Int wherever possible otherwise.

C Type

Swift Type



char, signed char


unsigned char




unsigned short




unsigned int




unsigned long


long long


unsigned long long












Global Constants

Global constants defined in C and Objective-C source files are automatically imported by the Swift compiler as Swift global constants.

Imported Constant Enumerations and Structures

In Objective-C, constants are often used to provide a list of possible values for properties and method parameters. You can annotate an Objective-C typedef declaration with the NS_STRING_ENUM or NS_EXTENSIBLE_STRING_ENUM macro to have constants of that type imported by Swift as members of a common type. Use NS_STRING_ENUM for sets of values that can’t logically have additional values added by code in adopting modules. Use NS_EXTENSIBLE_STRING_ENUM for sets of values which may be expanded in a Swift extension.

Constants that represent a fixed set of possible values can be imported as a structure by adding the NS_STRING_ENUM macro. For example, consider the following Objective-C declarations for string constants of type TrafficLightColor:

  1. typedef NSString * TrafficLightColor NS_STRING_ENUM;
  2. TrafficLightColor const TrafficLightColorRed;
  3. TrafficLightColor const TrafficLightColorYellow;
  4. TrafficLightColor const TrafficLightColorGreen;

Here’s how Swift imports them:

  1. struct TrafficLightColor: RawRepresentable {
  2. typealias RawValue = String
  3. init(rawValue: RawValue)
  4. var rawValue: RawValue { get }
  5. static var red: TrafficLightColor { get }
  6. static var yellow: TrafficLightColor { get }
  7. static var green: TrafficLightColor { get }
  8. }

Constants that represent an extensible set of possible values can be imported as a structure by adding the NS_EXTENSIBLE_STRING_ENUM macro. For example, consider the following Objective-C declarations for string constants of type StateOfMatter:

  1. typedef NSString * StateOfMatter NS_EXTENSIBLE_STRING_ENUM;
  2. StateOfMatter const StateOfMatterSolid;
  3. StateOfMatter const StateOfMatterLiquid;
  4. StateOfMatter const StateOfMatterGas;

Here’s how Swift imports them:

  1. struct StateOfMatter: RawRepresentable {
  2. typealias RawValue = String
  3. init(_ rawValue: RawValue)
  4. init(rawValue: RawValue)
  5. var rawValue: RawValue { get }
  6. static var solid: StateOfMatter { get }
  7. static var liquid: StateOfMatter { get }
  8. static var gas: StateOfMatter { get }
  9. }

Constants using this extensible form are imported with an additional initializer that lets callers omit the argument label when extending the set with new values.

Constants imported from a type declaration marked with the NS_EXTENSIBLE_STRING_ENUM macro can be extended in Swift code to add new values.

  1. extension StateOfMatter {
  2. static var plasma: StateOfMatter {
  3. return StateOfMatter("plasma")
  4. }
  5. }


Swift imports any function declared in a C header as a Swift global function. For example, consider the following C function declarations:

  1. int product(int multiplier, int multiplicand);
  2. int quotient(int dividend, int divisor, int *remainder);
  3. struct Point2D createPoint2D(float x, float y);
  4. float distance(struct Point2D from, struct Point2D to);

Here’s how Swift imports them:

  1. func product(_ multiplier: Int32, _ multiplicand: Int32) -> Int32
  2. func quotient(_ dividend: Int32, _ divisor: Int32, _ remainder: UnsafeMutablePointer<Int32>) -> Int32
  3. func createPoint2D(_ x: Float, _ y: Float) -> Point2D
  4. func distance(_ from: Point2D, _ to: Point2D) -> Float

Variadic Functions

In Swift, you can call C variadic functions, such as vasprintf, using the getVaList(_:) or withVaList(_:_:) functions. The getVaList(_:) function takes an array of CVarArg values and returns a CVaListPointer value, whereas the withVaList(_:_:) provides this value within the body a closure parameter rather than returning it directly. The resulting CVaListPointer value is then passed to the va_list argument of the C variadic function.

For example, here’s how to call the vasprintf function in Swift:

  1. func swiftprintf(format: String, arguments: CVarArg...) -> String? {
  2. return withVaList(arguments) { va_list in
  3. var buffer: UnsafeMutablePointer<Int8>? = nil
  4. return format.withCString { CString in
  5. guard vasprintf(&buffer, CString, va_list) != 0 else {
  6. return nil
  7. }
  8. return String(validatingUTF8: buffer!)
  9. }
  10. }
  11. }
  12. print(swiftprintf(format: "√2 ≅ %g", arguments: sqrt(2.0))!)
  13. // Prints "√2 ≅ 1.41421"


Swift imports any C structure declared in a C header as a Swift structure. The imported Swift structure contains a stored property for each C structure field and an initializer whose parameters correspond to the stored properties. If all of the imported members have default values, Swift also provides a default initializer that takes no arguments. For example, given the following C structure:

  1. struct Color {
  2. float r, g, b;
  3. };
  4. typedef struct Color Color;

Here’s the corresponding Swift type:

  1. public struct Color {
  2. var r: Float
  3. var g: Float
  4. var b: Float
  5. init()
  6. init(r: Float, g: Float, b: Float)
  7. }

Importing Functions as Type Members

C APIs, such as the Core Foundation framework, often provide functions that create, access, or modify C structures. You can use the CF_SWIFT_NAME macro in your own code to have Swift import C functions as members of the imported structure type. For example, given the following C function declarations:

  1. Color ColorCreateWithCMYK(float c, float m, float y, float k) CF_SWIFT_NAME(Color.init(c:m:y:k:));
  2. float ColorGetHue(Color color) CF_SWIFT_NAME(getter:Color.hue(self:));
  3. void ColorSetHue(Color color, float hue) CF_SWIFT_NAME(setter:Color.hue(self:newValue:));
  4. Color ColorDarkenColor(Color color, float amount) CF_SWIFT_NAME(Color.darken(self:amount:));
  5. extern const Color ColorBondiBlue CF_SWIFT_NAME(Color.bondiBlue);
  6. Color ColorGetCalibrationColor(void) CF_SWIFT_NAME(getter:Color.calibration());
  7. Color ColorSetCalibrationColor(Color color) CF_SWIFT_NAME(setter:Color.calibration(newValue:));

Here’s how Swift imports them as type members:

  1. extension Color {
  2. init(c: Float, m: Float, y: Float, k: Float)
  3. var hue: Float { get set }
  4. func darken(amount: Float) -> Color
  5. static var bondiBlue: Color
  6. static var calibration: Color
  7. }

The argument passed to the CF_SWIFT_NAME macro uses the same syntax as the #selector expression. The use of self in a CF_SWIFT_NAME argument is used for instance methods to refer to the method receiver.


Swift imports any C enumeration marked with the NS_ENUM macro as a Swift enumeration with an Int raw value type. The prefixes to C enumeration case names are removed when they are imported into Swift, whether they’re defined in system frameworks or in custom code.

For example, the C enumeration below is declared using the NS_ENUM macro.

  1. typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
  2. UITableViewCellStyleDefault,
  3. UITableViewCellStyleValue1,
  4. UITableViewCellStyleValue2,
  5. UITableViewCellStyleSubtitle
  6. };

In Swift, it’s imported like this:

  1. enum UITableViewCellStyle: Int {
  2. case `default`
  3. case value1
  4. case value2
  5. case subtitle
  6. }

When you refer to an enumeration value, use the value name with a leading dot (.).

  1. let cellStyle: UITableViewCellStyle = .default

A C enumeration that is not marked with the NS_ENUM or NS_OPTIONS macro is imported as a Swift structure. Each member of the C enumeration is imported as a global read-only computed property of the structure’s type—not as a member of the Swift structure itself.

For example, the following C enumeration is not declared using the NS_ENUM macro:

  1. typedef enum {
  2. MessageDispositionUnread = 0,
  3. MessageDispositionRead = 1,
  4. MessageDispositionDeleted = -1,
  5. } MessageDisposition;

In Swift, it’s imported like this:

  1. struct MessageDisposition: RawRepresentable, Equatable {}
  2. var MessageDispositionUnread: MessageDisposition { get }
  3. var MessageDispositionRead: MessageDisposition { get }
  4. var MessageDispositionDeleted: MessageDisposition { get }

Swift automatically synthesizes conformance to the Equatable protocol for imported C enumeration types.

Option Sets

Swift also imports C enumerations marked with the NS_OPTIONS macro as a Swift option set. Option sets behave similarly to imported enumerations by truncating their prefixes to option value names.

For example, consider the following Objective-C options declaration:

  1. typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
  2. UIViewAutoresizingNone = 0,
  3. UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
  4. UIViewAutoresizingFlexibleWidth = 1 << 1,
  5. UIViewAutoresizingFlexibleRightMargin = 1 << 2,
  6. UIViewAutoresizingFlexibleTopMargin = 1 << 3,
  7. UIViewAutoresizingFlexibleHeight = 1 << 4,
  8. UIViewAutoresizingFlexibleBottomMargin = 1 << 5
  9. };

In Swift, it’s imported like this:

  1. public struct UIViewAutoresizing : OptionSet {
  2. public init(rawValue: UInt)
  3. public static var flexibleLeftMargin: UIViewAutoresizing { get }
  4. public static var flexibleWidth: UIViewAutoresizing { get }
  5. public static var flexibleRightMargin: UIViewAutoresizing { get }
  6. public static var flexibleTopMargin: UIViewAutoresizing { get }
  7. public static var flexibleHeight: UIViewAutoresizing { get }
  8. public static var flexibleBottomMargin: UIViewAutoresizing { get }
  9. }

In Objective-C, an option set is a bit mask of integer values. You use the bitwise OR operator (|) to combine option values, and the bitwise AND operator (&) to check for option values. You create a new option set from a constant value or expression, An empty option set is represented by the constant zero (0).

In Swift, option sets are represented by structures conforming to the OptionSet protocol, with static variables for each option value. You can create a new option set value using an array literal, and access option values with a leading dot (.), similar to an enumeration. An empty option set can be created from an empty array literal ([]) or by calling its default initializer.

Option sets behave like Swift’s Set collection type. You use the insert(_:) or formUnion(_:) methods to add option values, the remove(_:) or subtract(_:) methods to remove option values, and the contains(_:) method to check for an option value.

  1. let options: Data.Base64EncodingOptions = [
  2. .lineLength64Characters,
  3. .endLineWithLineFeed
  4. ]
  5. let string = data.base64EncodedString(options: options)


Swift imports C unions as Swift structures. Although Swift doesn’t support unions, a C union imported as a Swift structure still behaves like a C union. For example, consider a C union named SchroedingersCat that has an isAlive and an isDead field:

  1. union SchroedingersCat {
  2. bool isAlive;
  3. bool isDead;
  4. };

In Swift, it’s imported like this:

  1. struct SchroedingersCat {
  2. var isAlive: Bool { get set }
  3. var isDead: Bool { get set }
  4. init(isAlive: Bool)
  5. init(isDead: Bool)
  6. init()
  7. }

Because unions in C use the same base memory address for all of their fields, all of the computed properties in a union imported by Swift use the same underlying memory. As a result, changing the value of any computed property on an instance of the imported structure changes the value of all other properties defined by that structure.

In the example below, changing the value of the isAlive computed property on an instance of the SchroedingersCat structure also changes the value of the instance’s isDead computed property:

  1. var mittens = SchroedingersCat(isAlive: false)
  2. print(mittens.isAlive, mittens.isDead)
  3. // Prints "false false"
  4. mittens.isAlive = true
  5. print(mittens.isDead)
  6. // Prints "true"

Bit Fields

Swift imports bit fields in structures, such those found in Foundation’s NSDecimal type, as computed properties. When accessing a computed property corresponding to a bit field, Swift automatically converts the value to and from compatible Swift types.

Unnamed Structure and Union Fields

C struct and union types can define fields that have no name or that are of an unnamed type. Unnamed fields consist of a nested struct or union type with named fields.

For example, consider a C structure named Cake that contains the fields layers and height nested within an unnamed union type, and a field toppings of an unnamed struct type:

  1. struct Cake {
  2. union {
  3. int layers;
  4. double height;
  5. };
  6. struct {
  7. bool icing;
  8. bool sprinkles;
  9. } toppings;
  10. };

After the Cake structure has been imported, you can initialize it and use it as follows:

  1. var simpleCake = Cake()
  2. simpleCake.layers = 5
  3. print(simpleCake.toppings.icing)
  4. // Prints "false"

The Cake structure is imported with a memberwise initializer that you can use to initialize the structure with custom values for its fields, as seen below:

  1. let cake = Cake(
  2. .init(layers: 2),
  3. toppings: .init(icing: true, sprinkles: false)
  4. )
  5. print("The cake has \(cake.layers) layers.")
  6. // Prints "The cake has 2 layers."
  7. print("Does it have sprinkles?", cake.toppings.sprinkles ? "Yes." : "No.")
  8. // Prints "Does it have sprinkles? No."

Because the first field of the Cake structure is unnamed, its initializer’s first parameter does not have a label. Because the Cake structure has fields with unnamed types, you use the .init initializers, which are picked using type inference, to set the initial value for each of the structure’s unnamed fields.


Whenever possible, Swift avoids giving you direct access to pointers. There are, however, various pointer types available for your use when you need direct access to memory. The following tables use Type as a placeholder type name to indicate syntax for the mappings.

For return types, variables, and arguments, the following mappings apply:

C Syntax

Swift Syntax

const Type *


Type *


For class types, the following mappings apply:

C Syntax

Swift Syntax

Type * const *


Type * __strong *


Type **


For pointers to untyped, raw memory, the following mappings apply:

C Syntax

Swift Syntax

const void *


void *


Swift also provides pointer types for working with buffers, which are discussed in Buffer Pointers.

If the type of the value pointed to by a C pointer cannot be represented by Swift, such as an incomplete struct type, the pointer is imported as an OpaquePointer.

Constant Pointers

When a function is declared as taking an UnsafePointer<Type> argument, it can accept any of the following:

  • An UnsafePointer<Type>, UnsafeMutablePointer<Type>, or AutoreleasingUnsafeMutablePointer<Type> value, which is converted to UnsafePointer<Type> if necessary.

  • A String value, if Type is Int8 or UInt8. The string will automatically be converted to UTF8 in a buffer, and a pointer to that buffer is passed to the function.

  • An in-out expression that contains a mutable variable, property, or subscript reference of type Type, which is passed as a pointer to the address of the left-hand side identifier.

  • A [Type] value, which is passed as a pointer to the start of the array.

The pointer passed to the function is guaranteed to be valid only for the duration of the function call. Don’t try to persist the pointer and access it after the function has returned.

If you have declared a function like this one:

  1. func takesAPointer(_ p: UnsafePointer<Float>) {
  2. // ...
  3. }

You can call it in any of the following ways:

  1. var x: Float = 0.0
  2. takesAPointer(&x)
  3. takesAPointer([1.0, 2.0, 3.0])

When a function is declared as taking an UnsafeRawPointer argument, it can accept the same operands as UnsafePointer<Type> for any type Type.

If you have declared a function like this one:

  1. func takesARawPointer(_ p: UnsafeRawPointer?) {
  2. // ...
  3. }

You can call it in any of the following ways:

  1. var x: Float = 0.0, y: Int = 0
  2. takesARawPointer(&x)
  3. takesARawPointer(&y)
  4. takesARawPointer([1.0, 2.0, 3.0] as [Float])
  5. let intArray = [1, 2, 3]
  6. takesARawPointer(intArray)

Mutable Pointers

When a function is declared as taking an UnsafeMutablePointer<Type> argument, it can accept any of the following:

  • An UnsafeMutablePointer<Type> value

  • An in-out expression of type Type that contains a mutable variable, property, or subscript reference, which is passed as a pointer to the address of the mutable value.

  • An in-out expression of type [Type] that contains a mutable variable, property, or subscript reference, which is passed as a pointer to the start of the array, and is lifetime-extended for the duration of the call

If you have declared a function like this one:

  1. func takesAMutablePointer(_ p: UnsafeMutablePointer<Float>) {
  2. // ...
  3. }

You can call it in any of the following ways:

  1. var x: Float = 0.0
  2. var a: [Float] = [1.0, 2.0, 3.0]
  3. takesAMutablePointer(&x)
  4. takesAMutablePointer(&a)

When a function is declared as taking an UnsafeMutableRawPointer argument, it can accept the same operands as UnsafeMutablePointer<Type> for any type Type.

If you have declared a function like this one:

  1. func takesAMutableRawPointer(_ p: UnsafeMutableRawPointer?) {
  2. // ...
  3. }

You can call it in any of the following ways:

  1. var x: Float = 0.0, y: Int = 0
  2. var a: [Float] = [1.0, 2.0, 3.0], b: [Int] = [1, 2, 3]
  3. takesAMutableRawPointer(&x)
  4. takesAMutableRawPointer(&y)
  5. takesAMutableRawPointer(&a)
  6. takesAMutableRawPointer(&b)

Autoreleasing Pointers

When a function is declared as taking an AutoreleasingUnsafeMutablePointer<Type>, it can accept any of the following:

  • An AutoreleasingUnsafeMutablePointer<Type> value

  • An in-out expression that contains a mutable variable, property, or subscript reference of type Type, which is copied bitwise into a temporary nonowning buffer. The address of that buffer is passed to the callee, and on return, the value in the buffer is loaded, retained, and reassigned into the operand.

Note that this list does not include arrays.

If you have declared a function like this one:

  1. func takesAnAutoreleasingPointer(_ p: AutoreleasingUnsafeMutablePointer<NSDate?>) {
  2. // ...
  3. }

You can call it in the following way:

  1. var x: NSDate? = nil
  2. takesAnAutoreleasingPointer(&x)

Types that are pointed to are not bridged. For example, NSString ** comes over to Swift as AutoreleasingUnsafeMutablePointer<NSString?>, not AutoreleasingUnsafeMutablePointer<String?>.

Function Pointers

C function pointers are imported into Swift as closures with C function pointer calling convention, denoted by the @convention(c) attribute. For example, a function pointer that has the type int (*)(void) in C is imported into Swift as @convention(c) () -> Int32.

When calling a function that takes a function pointer argument, you can pass a top-level Swift function, a closure literal, or nil. You can also pass a closure property of a generic type or a generic method as long as no generic type parameters are referenced in the closure’s argument list or body. For example, consider Core Foundation’s CFArrayCreateMutable(_:_:_:) function. The CFArrayCreateMutable(_:_:_:) function takes a CFArrayCallBacks structure, which is initialized with function pointer callbacks:

  1. func customCopyDescription(_ p: UnsafeRawPointer?) -> Unmanaged<CFString>? {
  2. // return an Unmanaged<CFString>? value
  3. }
  4. var callbacks = CFArrayCallBacks(
  5. version: 0,
  6. retain: nil,
  7. release: nil,
  8. copyDescription: customCopyDescription,
  9. equal: { (p1, p2) -> DarwinBoolean in
  10. // return Bool value
  11. }
  12. )
  13. var mutableArray = CFArrayCreateMutable(nil, 0, &callbacks)

In the example above, the CFArrayCallBacks initializer uses nil values as arguments for the retain and release parameters, the customCopyDescription(_:) function as the argument for the customCopyDescription parameter, and a closure literal as the argument for the equal parameter.

Buffer Pointers

A buffer pointer is used for low-level access to a region of memory. For example, you can use a buffer pointer for efficent processing and communication of data between apps and services.

Swift has the following buffer pointer types:

  • UnsafeBufferPointer

  • UnsafeMutableBufferPointer

  • UnsafeRawBufferPointer

  • UnsafeMutableRawBufferPointer

The typed buffer pointer types, UnsafeBufferPointer and UnsafeMutableBufferPointer, let you view or mutate a contiguous block of memory. These types let you access the memory as a collection, where each item is an instance of the buffer type’s Element generic type parameter.

The raw buffer pointer types, UnsafeRawBufferPointer and UnsafeMutableRawBufferPointer, let you view or mutate a contiguous block of memory as a collection of UInt8 values, where each value corresponds to a byte of memory. These types let you use low-level programming patterns, such as operating on raw memory without compiler-checked type safety, or switching between several different typed interpretations of the same memory.

Null Pointers

In Objective-C, a pointer type declaration can use the _Nullable and _Nonnull annotations to specify whether or not that pointer may have a nil or NULL value. In Swift, a null pointer is represented by a nil value of an optional pointer type. Pointer initializers taking the integer representation of an address in memory are failable. A non-optional pointer type cannot be assigned a nil value.

The following mappings apply:

Objective-C Syntax

Swift Syntax

const Type * _Nonnull


const Type * _Nullable


const Type * _Null_unspecified


Pointer Arithmetic

When working with opaque data types, you may need to perform unsafe pointer operations. You can use the arithmetic operators on Swift pointer values to create new pointers at a specified offset.

  1. let pointer: UnsafePointer<Int8>
  2. let offsetPointer = pointer + 24
  3. // offsetPointer is 24 strides ahead of pointer

Data Type Size Calculation

In C, the sizeof and alignof operators return the size and alignment of any variable or data type, In Swift, you use MemoryLayout<T> to access the memory layout of the parameterized type T through the size, stride, and alignment properties. For example, the timeval structure in Darwin has a size and stride of 16 and an alignment of 8:

  1. print(MemoryLayout<timeval>.size)
  2. // Prints "16"
  3. print(MemoryLayout<timeval>.stride)
  4. // Prints "16"
  5. print(MemoryLayout<timeval>.alignment)
  6. // Prints "8"

You use this when calling C functions from Swift that take the size of a type or value as an argument. For example, the setsockopt(_:_:_:_:_:) function can specify a timeval value as a receive timeout option (SO_RCVTIMEO) for a socket by passing a pointer to that value and the length of that value:

  1. let sockfd = socket(AF_INET, SOCK_STREAM, 0)
  2. var optval = timeval(tv_sec: 30, tv_usec: 0)
  3. let optlen = socklen_t(MemoryLayout<timeval>.size)
  4. if setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &optval, optlen) == 0 {
  5. // ...
  6. }

For more information, see the MemoryLayout<T> API reference.

One-Time Initialization

In C, the pthread_once() function in POSIX and the dispatch_once() and dispatch_once_f() functions in Grand Central Dispatch provide mechanisms for executing initialization code exactly once. In Swift, global constants and stored type properties are guaranteed to be initialized only once, even when accessed across multiple threads simultaneously. Because this functionality is provided through language features, the corresponding POSIX and Grand Central Dispatch C function calls are not exposed by Swift.

Preprocessor Directives

The Swift compiler does not include a preprocessor. Instead, it takes advantage of compile-time attributes, conditional compilation blocks, and language features to accomplish the same functionality. For this reason, preprocessor directives are not imported in Swift.

Simple Macros

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

Complex macros are used in C and Objective-C but have no counterpart in Swift. Complex macros are macros that do not define constants, including parenthesized, function-like macros. You use complex macros in C and Objective-C to avoid type-checking constraints or to avoid retyping large amounts of boilerplate code. However, macros can make debugging and refactoring difficult. In Swift, you can use functions and generics to achieve the same results without any compromises. Therefore, the complex macros that are in C and Objective-C source files are not made available to your Swift code.

Conditional Compilation Blocks

Swift code and Objective-C code are conditionally compiled in different ways. Swift code can be conditionally compiled using conditional compilation blocks. For example, if you compile the code below using swift -D DEBUG_LOGGING to set the DEBUG_LOGGING conditional compilation flag, the compiler includes the code in the body of the conditional complication block.

  2. print("Flag enabled.")
  3. #endif

A compilation condition can include the literal true and false values, custom conditional compilation flags (specified using -D <#flag#>), and the platform conditions listed in the table below.


Valid arguments


macOS, iOS, watchOS, tvOS, Linux


x86_64, arm, arm64, i386


>= followed by a version number

You can combine compilation conditions using the && and || operators, negate them with the ! operator, and add branches with #elseif and #else compilation directives. You can also nest conditional compilation blocks within other conditional compilation blocks.

  1. #if arch(arm) || arch(arm64)
  2. #if swift(>=3.0)
  3. print("Using Swift 3 ARM code")
  4. #else
  5. print("Using Swift 2.2 ARM code")
  6. #endif
  7. #elseif arch(x86_64)
  8. print("Using 64-bit x86 code.")
  9. #else
  10. print("Using general code.")
  11. #endif

In contrast with condition compilation in the C preprocessor, conditional compilation blocks in Swift must completely surround blocks of code that are self-contained and syntactically valid. This is because all Swift code is syntax checked, even when it is not compiled. However, there is an exception if the compilation condition includes a swift() platform condition: The statements are parsed only if the compiler’s version of Swift matches what is specified in the platform condition. This exception ensures that an older compiler doesn’t attempt to parse syntax introduced in a newer version of Swift.