Swift
C++
Objective-C
interoperability
programming

Can I mix Swift with C? Like the Objective-C .mm files

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Swift is not like Objective-C++ in the sense of having a special source-file mode where you freely mix Swift syntax and C or C++ syntax in one file. You can interoperate with C directly and, in modern toolchains, with some C++ APIs as well, but there is no direct Swift equivalent of a .mm file that literally embeds both languages together.

What Objective-C++ .mm Files Actually Do

An Objective-C++ file works because Objective-C and C++ share a compatible compilation model. A .mm file tells Clang to parse one source file as a language mode that understands both Objective-C constructs and C++ constructs.

Swift has a different compiler and language model. You do not switch a .swift file into a "Swift plus C++" parsing mode.

So the answer to the literal question is no: there is no Swift-file equivalent of .mm.

Direct Interop with C

Swift interoperates with C very well, but it does so through imported headers or modules, not by embedding raw C code inside the Swift file.

For example, if you have a C header:

c
// math_utils.h
int add_ints(int a, int b);

and implementation:

c
1// math_utils.c
2int add_ints(int a, int b) {
3    return a + b;
4}

Swift can call it after the header is exposed through a bridging header or module:

swift
let result = add_ints(2, 3)
print(result)

That is real Swift-to-C interop, but the languages still live in separate source files.

What About C++?

Historically, the standard answer was to wrap C++ in Objective-C++ and expose an Objective-C-friendly interface to Swift. That is still a common pattern because it gives you explicit control over what crosses the boundary.

For example, a wrapper might look like this:

objective-c
1// EngineWrapper.h
2@interface EngineWrapper : NSObject
3- (int)addNumber:(int)value toNumber:(int)other;
4@end
objective-c++
1// EngineWrapper.mm
2#import "EngineWrapper.h"
3#include "Engine.hpp"
4
5@implementation EngineWrapper
6- (int)addNumber:(int)value toNumber:(int)other {
7    Engine engine;
8    return engine.add(value, other);
9}
10@end

Swift then calls the wrapper as normal Objective-C API.

Modern Swift toolchains do have growing C++ interoperability support, but that still does not mean "write Swift and C++ mixed together in one file." It means Swift can import and use some C++ APIs directly under supported conditions.

Choosing the Right Bridge

Use direct C interop when the library already exposes a stable C API.

Use an Objective-C++ wrapper when:

  • the C++ API is complex
  • templates or advanced C++ types are involved
  • you want a narrow, stable interface for Swift

Use modern direct Swift-to-C++ interop only when the toolchain and the target API shape support it cleanly and you are comfortable with the constraints.

Common Pitfalls

The biggest pitfall is expecting to paste C or C++ syntax into a .swift file and have the compiler understand it the way a .mm file works. Swift does not have that source-file mode.

Another issue is exposing too much C++ detail across the language boundary. Even when interop is possible, keeping the boundary narrow usually makes the build simpler and the Swift API easier to use.

Developers also sometimes treat Objective-C++ wrappers as a legacy-only hack. In many real projects, they are still the clearest and most maintainable way to bridge non-trivial C++ code into Swift.

Finally, be careful with memory ownership and string or collection conversions at the boundary. Interop works best when the exposed interface uses simple, predictable types.

Summary

  • Swift does not have a .mm-style mixed-language source file mode.
  • Swift can call C through imported headers or modules.
  • C++ is commonly bridged through Objective-C++ wrappers, and modern Swift also supports some direct C++ interop.
  • Even with direct interop, Swift and C++ are still not mixed syntactically in one source file.
  • Keep the language boundary small and explicit for the most maintainable design.

Course illustration
Course illustration

All Rights Reserved.