Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2025 Swift

WWDC25 · 32 min · 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 ↗

Transcript all transcripts

Chapters

Code shown on screen · 23 snippets

Unsafety can be subtle swift · at 3:19 ↗
// Swift
var imageData = [UInt8](repeating: 0, count: imageDataSize)
filterImage(&imageData, imageData.count)
Strict memory safety swift · at 4:01 ↗
// 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 cpp · at 8:00 ↗
// C/C++
void invertImage(uint8_t *imagePtr, size_t imageSize);
Raw pointers don't prevent out-of-bounds errors swift · at 8:21 ↗
// Swift
var imageData = [UInt8](repeating: 0, count: imageSize)
invertImage(&imageData, imageSize)
Raw pointers don't prevent out-of-bounds errors swift · at 8:30 ↗
// Swift
var imageData = [UInt8](repeating: 0, count: imageSize)
invertImage(&imageData, 1000000000000)
Solution for out-of-bounds error swift · at 8:48 ↗
// Swift
func invertImage(_ imagePtr : inout MutableSpan<UInt8>)
Solution for out-of-bounds error swift · at 8:54 ↗
// Swift
var imageDataSpan = imageData.mutableSpan
invertImage(&imageDataSpan)
Express bounds information using __counted_by cpp · at 9:58 ↗
// C/C++
void invertImage(uint8_t *__counted_by(imageSize) imagePtr __noescape, size_t imageSize);
Unsafe function declaration taking a C++ span cpp · at 12:10 ↗
// C++
using CxxSpanOfByte = std::span<uint8_t>;
void applyGrayscale(CxxSpanOfByte imageView);
Unsafe C++ function caching a C++ span cpp · at 13:21 ↗
// C++
CxxSpanOfByte cachedView;
void applyGrayscale(CxxSpanOfByte imageView) {
  cachedView = imageView;
  // Apply effect on image ...
}
Swift Span prevents escaping scope swift · at 14:08 ↗
// 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 cpp · at 15:18 ↗
// C++
CxxSpanOfByte cachedView;
void applyGrayscale(CxxSpanOfByte imageView __noescape) {
  // Apply effect on image ...
}
Safely use a C++ Span as a Swift Span swift · at 15:56 ↗
// Swift
var imageDataSpan = &imageData.mutableSpan
applyGrayscale(&imageDataSpan)
Returned C++ Span is unsafe cpp · at 17:21 ↗
// C++
CxxSpanOfByte scanImageRow(CxxSpanOfByte imageView,
                           size_t width, size_t rowIndex);
Swift Spans prevent use-after-free by design swift · at 18:06 ↗
// 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 cpp · at 18:47 ↗
// C++
CxxSpanOfByte scanImageRow(CxxSpanOfByte imageView __lifetimebound,
                           size_t width, size_t rowIndex);
Safely return a C++ Span as a Swift Span swift · at 18:50 ↗
// Swift
var imageDataSpan = imageData.mutableSpan
var rowView = scanImageRow(&imageDataSpan, width, y)
Import a C++ view type as SWIFT_NONESCAPABLE cpp · at 22:29 ↗
// C++
struct ImageView {
  std::span<uint8_t> pixelBytes;
  int width;
  int height;
} SWIFT_NONESCAPABLE;
Import a C++ reference-counted type cpp · at 23:31 ↗
// 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 cpp · at 23:57 ↗
// C++
ImageBuffer *_Nonnull createImage() SWIFT_RETURNS_RETAINED;
ImageBuffer *_Nonnull getCachedImage() SWIFT_RETURNS_UNRETAINED;
C++ standard library hardening cpp · at 27:51 ↗
// 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 cpp · at 28:59 ↗
// 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 cpp · at 30:11 ↗
// 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