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:
// MyApp-Bridging-Header.h
#import "LegacyAPI.h"
Example: Swift Usage:
// 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:
@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:
// MyViewController.m
TaskManager *manager = [[TaskManager alloc] initWithTaskName:@"Demo"];
manager.taskName = @"NewName";
[manager startTask];
Bridged Types
Swift and Objective-C share bridged types:
String
↔NSString
Array
↔NSArray
Dictionary
↔NSDictionary
Example:
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:
// math_utils.h
int multiply(int a, int b);
// Import module
import MathUtils
let result = multiply(5, 3) // 15
Example: C Struct:
struct Point {
double x, y;
};
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:
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:
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
// 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