YourProject-Bridging-Header.h and configure its path in Target Build Settings under ‘Objective-C Bridging Header’. Add #import "YourClass.h" to this header, and your Objective-C types become immediately available in Swift without explicit import statements.Why this happens
Swift and Objective-C run on the same Objective-C runtime and use the same object model, but they are distinct languages with different type systems and compilers. When you create an Objective-C class, Swift cannot read raw .h header files on its own unless the Clang importer processes them into Swift-compatible interfaces.
In standard Xcode application targets, the Clang importer relies on an Objective-C Bridging Header. Any header listed inside this bridging file is parsed during compilation and exposed globally across all Swift files in that target. Without this bridge configured, attempting to instantiate an Objective-C class or invoke its methods inside a Swift file causes a compiler error: Cannot find type 'YourClass' in scope.
For framework targets or Swift packages, bridging headers are explicitly forbidden by Xcode. In those environments, Clang instead uses modular headers via module maps (module.modulemap) or public umbrella headers so that Swift can consume Objective-C through standard import ModuleName statements.
How to fix it
1. Let Xcode generate the bridging header automatically
The fastest, least error-prone way to establish bridging in an app target is to trigger Xcode’s automated workflow.
- In Xcode, right-click your project group and choose File > New > File… (or press
Cmd + N). - Select Cocoa Touch Class (or an empty Objective-C File) under iOS, name it (for example,
CustomObject.m), and save it into your app target. - Xcode displays a modal prompt asking: “Would you like to configure an Objective-C bridging header?”
- Click Create Bridging Header.
Xcode automatically creates a file named YourTargetName-Bridging-Header.h and registers its relative path in your target’s Build Settings under Objective-C Bridging Header (SWIFT_OBJC_BRIDGING_HEADER).
2. Configure a bridging header manually
If you skipped the prompt, deleted the bridging header, or need to connect legacy files to a new Swift project, configure the file by hand:
- In Xcode’s Project Navigator, select File > New > File….
- Choose Header File (
.h) under the iOS or macOS tab. - Name the file
YourProject-Bridging-Header.hand ensure your app target is selected. - Select your app target in the project editor, click Build Settings, and ensure the All filter is active.
- Search for
Objective-C Bridging Header. - Double-click the setting field and enter the path to your header file. Use the
$(SRCROOT)build variable to prevent path breakage across different machines:
$(SRCROOT)/YourProject/YourProject-Bridging-Header.h
3. Expose the Objective-C headers and call the code in Swift
Once the bridging header exists, expose your Objective-C types to the compiler:
Step A: Define your Objective-C class
In CustomObject.h:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CustomObject : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, assign) NSInteger count;
- (instancetype)initWithTitle:(NSString *)title count:(NSInteger)count;
- (NSString *)formatSummary;
- (void)performActionWithCompletion:(void (^)(BOOL success))completion;
@end
NS_ASSUME_NONNULL_END
In CustomObject.m:
#import "CustomObject.h"
@implementation CustomObject
- (instancetype)initWithTitle:(NSString *)title count:(NSInteger)count {
self = [super init];
if (self) {
_title = [title copy];
_count = count;
}
return self;
}
- (NSString *)formatSummary {
return [NSString stringWithFormat:@"%@: %ld items", self.title, (long)self.count];
}
- (void)performActionWithCompletion:(void (^)(BOOL success))completion {
// Simulate operation
if (completion) {
completion(YES);
}
}
@end
Step B: Import the header in the bridging file
Open YourProject-Bridging-Header.h and add the import:
// YourProject-Bridging-Header.h
#import "CustomObject.h"
Step C: Use the class in Swift
You do not need an import statement in your Swift files. The bridging header automatically exposes types to the entire compilation unit:
import SwiftUI
struct ContentView: View {
@State private var message: String = ""
var body: some View {
VStack(spacing: 16) {
Text(message.isEmpty ? "Tap to run Objective-C" : message)
Button("Execute") {
runObjectiveCCode()
}
}
.padding()
}
private func runObjectiveCCode() {
// Objective-C initializers convert to Swift initializers
let customObj = CustomObject(title: "Inventory", count: 42)
// Properties bridge directly
customObj.title = "Updated Inventory"
// Method calls adapt to Swift argument labeling
let summary = customObj.formatSummary()
print(summary)
// Objective-C blocks map to Swift closures
customObj.performAction { success in
if success {
message = customObj.formatSummary()
}
}
}
}
4. How Swift transforms Objective-C APIs
The Clang importer translates Objective-C conventions into idiomatic Swift according to these rules:
- Initializers: Methods beginning with
init(e.g.,initWithTitle:count:) become Swift initializers (init(title:count:)). - Getters and Setters: Getter and setter method pairs collapse into standard read-write Swift properties.
- Error Handling: Methods with a final
(NSError **)errorparameter automatically translate into Swift throwing functions (throws). - Nullability: Headers wrapped in
NS_ASSUME_NONNULL_BEGINandNS_ASSUME_NONNULL_ENDbridgeNSString *to Swift’s non-optionalString. Without nullability macros, types bridge as Implicitly Unwrapped Optionals (String!). - Renaming: Use the
NS_SWIFT_NAMEmacro in your Objective-C header if the automatically translated Swift name looks unnatural:
- (void)fetchDataForUser:(NSString *)userId NS_SWIFT_NAME(fetchData(userId:));
5. Consuming Objective-C from frameworks or Swift packages
If your Objective-C code lives inside an Xcode framework target or a Swift Package, bridging headers cannot be used. You must structure the code into a module:
- Framework Targets: Under the framework target’s Build Settings, set Defines Module to
YES. Move your Objective-C header file to the Build Phases > Headers section and drag it into the Public category. In your Swift file, access the code using:
swift
import YourFrameworkName - Swift Packages: Place the Objective-C headers in an
include/directory within your target source tree. Swift Package Manager synthesizes a module map automatically, allowing any Swift target in the package to declare a dependency on that target and runimport MyObjCLibrary.
How to prevent it
- Always use
$(SRCROOT)in bridging paths: Never hardcode absolute disk paths (e.g.,/Users/username/...) in the Objective-C Bridging Header build setting. Hardcoded paths break builds on CI servers and other teammates’ machines. - Add nullability annotations to all C/Objective-C headers: Wrap your Objective-C
.hfiles withNS_ASSUME_NONNULL_BEGINandNS_ASSUME_NONNULL_END, explicitly marking optional pointers withnullable. This prevents dangerous implicitly unwrapped optionals (Type!) in Swift. - Build after editing headers: Xcode’s SourceKit indexer can lag behind header changes. Press
Cmd + Shift + Kto clean andCmd + Bto build if Xcode code completion fails to resolve an Objective-C class. - Keep bridging headers clean: Only import public interface headers that Swift code genuinely needs. Importing monolithic umbrella headers into a bridging header can significantly increase Swift compilation times.
FAQ
Can I use a bridging header inside a Swift dynamic framework?
No. Xcode disallows the SWIFT_OBJC_BRIDGING_HEADER setting in framework targets because frameworks must be modular and relocatable. For framework targets, mark your Objective-C headers as Public in the target’s Build Phases and set Defines Module (DEFINES_MODULE) to YES.
Do I need to import the bridging header in my Swift files?
No. The bridging header is passed directly to the Swift compiler during build time. Every declaration imported into YourProject-Bridging-Header.h is globally accessible to every Swift file within the same application target.
How do I call my Swift code from an Objective-C file?
Make your Swift class inherit from NSObject and mark exposed members with @objc or @objcMembers. Then, in your Objective-C .m file, add #import "YourTargetName-Swift.h". This header is automatically generated by Xcode and contains Objective-C interface declarations for your Swift types.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
