2025 Swift
Safely mix C, C++, and Swift
Learn how to mix C, C++, and Swift while improving the safety of your apps. We’ll show you how to find where unsafe C and C++ APIs are called in your Swift code, how to call them more safely, and how to make your app’s existing C and C++ code safer by default.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 23 snippets
Unsafety can be subtle
// Swift
var imageData = [UInt8](repeating: 0, count: imageDataSize)
filterImage(&imageData, imageData.count) Strict memory safety
// Swift
var imageData = [UInt8](repeating: 0, count: imageDataSize)
filterImage(&imageData, imageData.count)
//warning: Expression uses unsafe constructs but is not marked with 'unsafe' Raw pointers don't prevent out-of-bounds errors
// C/C++
void invertImage(uint8_t *imagePtr, size_t imageSize); Raw pointers don't prevent out-of-bounds errors
// Swift
var imageData = [UInt8](repeating: 0, count: imageSize)
invertImage(&imageData, imageSize) Raw pointers don't prevent out-of-bounds errors
// Swift
var imageData = [UInt8](repeating: 0, count: imageSize)
invertImage(&imageData, 1000000000000) Solution for out-of-bounds error
// Swift
func invertImage(_ imagePtr : inout MutableSpan<UInt8>) Solution for out-of-bounds error
// Swift
var imageDataSpan = imageData.mutableSpan
invertImage(&imageDataSpan) Express bounds information using __counted_by
// C/C++
void invertImage(uint8_t *__counted_by(imageSize) imagePtr __noescape, size_t imageSize); Unsafe function declaration taking a C++ span
// C++
using CxxSpanOfByte = std::span<uint8_t>;
void applyGrayscale(CxxSpanOfByte imageView); Unsafe C++ function caching a C++ span
// C++
CxxSpanOfByte cachedView;
void applyGrayscale(CxxSpanOfByte imageView) {
cachedView = imageView;
// Apply effect on image ...
} Swift Span prevents escaping scope
// Swift
var cachedView: MutableSpan<UInt8>?
func applyGrayscale(_ imageView: inout MutableSpan<UInt8>) {
cachedView = imageView // error: lifetime dependent value escapes its scope
// Apply effect on image ...
} Express lifetime information using __noescape
// C++
CxxSpanOfByte cachedView;
void applyGrayscale(CxxSpanOfByte imageView __noescape) {
// Apply effect on image ...
} Safely use a C++ Span as a Swift Span
// Swift
var imageDataSpan = &imageData.mutableSpan
applyGrayscale(&imageDataSpan) Returned C++ Span is unsafe
// C++
CxxSpanOfByte scanImageRow(CxxSpanOfByte imageView,
size_t width, size_t rowIndex); Swift Spans prevent use-after-free by design
// Swift
func scanImageRow(_ imageView : inout MutableSpan<UInt8>,
_ width : Int, _ rowIndex : Int) -> MutableSpan<UInt8>
// error: a function with a ~Escapable result requires '@lifetime(...)' Express lifetime dependency with __lifetimebound
// C++
CxxSpanOfByte scanImageRow(CxxSpanOfByte imageView __lifetimebound,
size_t width, size_t rowIndex); Safely return a C++ Span as a Swift Span
// Swift
var imageDataSpan = imageData.mutableSpan
var rowView = scanImageRow(&imageDataSpan, width, y) Import a C++ view type as SWIFT_NONESCAPABLE
// C++
struct ImageView {
std::span<uint8_t> pixelBytes;
int width;
int height;
} SWIFT_NONESCAPABLE; Import a C++ reference-counted type
// C++
struct ImageBuffer {
std::vector<uint8_t> data;
int width;
int height;
std::atomic<unsigned> refCount;
} SWIFT_SHARED_REFERENCE(retain_image_buffer, release_image_buffer);
void retain_image_buffer(ImageBuffer *_Nonnull buf);
void release_image_buffer(ImageBuffer *_Nonnull buf); Safely return a reference-counted type
// C++
ImageBuffer *_Nonnull createImage() SWIFT_RETURNS_RETAINED;
ImageBuffer *_Nonnull getCachedImage() SWIFT_RETURNS_UNRETAINED; C++ standard library hardening
// C++
void fill_array_with_indices(std::span<uint8_t> buffer) {
for (size_t i = 0; i < buffer.size(); ++i) {
buffer[i] = i;
}
} C++ unsafe buffer usage errors
// C++
void fill_array_with_indices(uint8_t *buffer, size_t count) {
for (size_t i = 0; i < count; ++i) {
buffer[i] = i; // error: unsafe buffer access
}
} Bounds safety extension for C
// C
void fill_array_with_indices(uint8_t *__counted_by(count) buf, size_t count) {
for (size_t i = 0; i < count; ++i) {
buf[i] = i;
}
} Resources
Related sessions
-
32 min