Skip to content

Interoperability

Swift interoperates seamlessly with Objective-C and C, enabling mixed-language projects, particularly in iOS/macOS development. This document covers calling Objective-C from Swift, exposing Swift to Objective-C, and C interop.

Objective-C Interoperability

Swift can call Objective-C APIs, and Objective-C can access Swift classes with proper annotations.

Calling Objective-C from Swift

Use a bridging header to import Objective-C headers.

Example: Bridging Header:

objc
// MyApp-Bridging-Header.h
#import "LegacyAPI.h"

Example: Swift Usage:

swift
// LegacyAPI.h
// @interface LegacyAPI: NSObject
// - (void)executeTask:(NSString *)taskName;
// @end

let api = LegacyAPI()
api.executeTask("Process") // Calls Objective-C

Exposing Swift to Objective-C

Use @objc and inherit from NSObject for classes, or use @objc for protocols and members.

Example:

swift
@objc protocol TaskDelegate {
    @objc optional func taskDidComplete()
}

@objc class TaskManager: NSObject {
    @objc var taskName: String
    weak var delegate: TaskDelegate?
    
    init(taskName: String) {
        self.taskName = taskName
    }
    
    @objc func startTask() {
        delegate?.taskDidComplete?()
    }
}

Example: Objective-C Usage:

objc
// MyViewController.m
TaskManager *manager = [[TaskManager alloc] initWithTaskName:@"Demo"];
manager.taskName = @"NewName";
[manager startTask];

Bridged Types

Swift and Objective-C share bridged types:

  • StringNSString
  • ArrayNSArray
  • DictionaryNSDictionary

Example:

swift
let nsString: NSString = "Swift"
let swiftString: String = nsString as String

C Interoperability

Swift can call C functions and use C structs directly.

Example: C Function:

c
// math_utils.h
int multiply(int a, int b);
swift
// Import module
import MathUtils

let result = multiply(5, 3) // 15

Example: C Struct:

c
struct Point {
    double x, y;
};
swift
var point = Point(x: 1.0, y: 2.0)
print(point.x) // 1.0

Importing C Modules

Use SPM or Xcode module maps to import C libraries.

Example: Module Map:

swift
module MyCLib {
    header "math_utils.h"
    export *
}

Memory Management in Interop

  • Swift ARC: Manages Swift objects.
  • Objective-C ARC: Compatible with Swift ARC.
  • C Memory: Manually manage (e.g., malloc, free).

Example: C Memory:

swift
let cString = strdup("Hello")
print(String(cString: cString!))
free(cString)

Limitations

  • No C++ Interop: Use C or Objective-C wrappers.
  • Optional Protocol Requirements: Require @objc.
  • Swift-Only Features: Generics, structs not fully exposed to Objective-C.

Best Practices

  • Minimize @objc: Use only for interop.
  • Use Bridged Types: Prefer Swift types in Swift code.
  • Test Interop: Verify mixed-language behavior.
  • Gradual Migration: Convert Objective-C to Swift incrementally.
  • Manual Memory: Carefully manage C allocations.
  • Module Maps: Organize C imports clearly.

Troubleshooting

  • Bridging Header Errors: Ensure header is in build settings.
  • Missing Symbols: Verify module imports.
  • Memory Leaks: Check C allocations.
  • Optional Protocol Issues: Use @objc for optional methods.
  • Type Mismatches: Cast bridged types correctly.

Example: Comprehensive Interoperability

swift
// Bridging-Header.h
// #import "LegacyAPI.h"

@objc protocol DataDelegate {
    func dataReceived(_ data: NSString)
}

@objc class DataProcessor: NSObject {
    weak var delegate: DataDelegate?
    
    func process() {
        let legacy = LegacyAPI()
        legacy.executeTask("Fetch")
        delegate?.dataReceived("Processed" as NSString)
    }
}

struct CPoint {
    var x, y: Double
}

func distance(_ p1: CPoint, _ p2: CPoint) -> Double {
    let cPoint1 = Point(x: p1.x, y: p1.y)
    let cPoint2 = Point(x: p2.x, y: p2.y)
    return sqrt(pow(cPoint1.x - cPoint2.x, 2) + pow(cPoint1.y - cPoint2.y, 2))
}

let processor = DataProcessor()
processor.delegate = SomeObjectiveCClass()
processor.process()

let p1 = CPoint(x: 0, y: 0)
let p2 = CPoint(x: 3, y: 4)
print(distance(p1, p2)) // 5.0

Released under the MIT License.