apple-cf
Safe Rust bindings for Apple's shared Core* frameworks — the foundation underneath the doom-fish macOS Rust suite. The only Rust dependency is the family's doom-fish-utils helper crate.
Status: Active correctness and FFI-contract maintenance. See
COVERAGE.mdfor what the safe layer covers and what the audit numbers inCOVERAGE_AUDIT.mdandCOVERAGE_AUDIT_V2.mddo and don't measure.
What's in the box
| Module | Framework | Feature flag | Status |
|---|---|---|---|
cf |
CoreFoundation values, collections, property lists, locale/formatter helpers, runtime primitives | — | ✅ |
raw |
Exhaustive low-level CoreFoundation/CoreMedia/CoreVideo/IOSurface/Dispatch bindings | — | ✅ |
cg |
CoreGraphics value types + bitmap drawing wrappers | cg |
✅ |
iosurface |
IOSurface (zero-copy GPU buffers, multi-planar formats) | iosurface |
✅ |
dispatch_queue |
Dispatch queues, async/apply helpers, groups, semaphores, timer sources | dispatch |
✅ |
cm |
CMTime, CMTimeRange, CMTimebase, CMSampleBuffer, CMBlockBuffer, CMFormatDescription, CMMetadataFormatDescription |
cm |
✅ |
cv |
CVBuffer, CVImageBuffer, CVPixelBuffer, CVPixelBufferPool, CVMetalTextureCache |
cv |
✅ |
utils |
Shared FFI helpers (always on) | — | ✅ |
Why this crate exists
Every doom-fish crate that wraps a media-adjacent Apple framework needs the same primitives — CFString, CGRect, CVPixelBuffer, IOSurface, dispatch queues, time values. Instead of re-vendoring those inside every crate (and drifting), this crate owns them once.
Safe Rust wrappers
├── CoreFoundation / Dispatch / CoreMedia / CoreVideo ergonomic APIs
├── exhaustive raw C bindings in `apple_cf::raw`
├── direct C FFI for value-only primitives where appropriate
└── Swift @_cdecl bridge for reference-counted / callback-heavy surfaces
Requirements
- macOS 13.0+
- Xcode toolchain (Swift 5.9+); Command Line Tools alone is not enough —
xcrun --sdk macosx --show-sdk-versionmust succeed - Apple Silicon or Intel Mac
Installation
[]
= "0.11"
Or enable only the Rust modules you use:
[]
= { = ">=0.11, <0.12", = false, = ["cg", "cm", "cv", "dispatch", "iosurface"] }
The cg, iosurface, dispatch, cm and cv features gate the Rust modules only. The Swift bridge is built as a single static library, so every build compiles all of it and links CoreFoundation, CoreGraphics, CoreMedia, CoreVideo, IOSurface, Metal and Foundation whichever features are enabled. The metal feature is a deprecated no-op.
Quick examples
Build CoreFoundation values and collections
use ;
let first = new;
let second = new;
let array = from_values;
assert_eq!;
Create a timer-backed dispatch source
use DispatchSource;
use thread;
use Duration;
let source = timer;
source.resume;
sleep;
source.cancel;
assert!;
Allocate an IOSurface-backed CVPixelBuffer
use CVPixelBuffer;
use IOSurface;
let surface = create.expect;
let pixel_buffer = create_with_io_surface.expect;
assert!;
assert_eq!;
CVPixelBuffer::create passes no attributes, so the buffer it returns is not guaranteed to be IOSurface-backed.
Architecture
This crate uses the same Swift-bridge pattern as the rest of the doom-fish crates:
swift-bridge/Sources/<Framework>Bridge/exposes thin@_cdeclentry pointssrc/ffi/*.rsdeclares the matchingextern "C"bindingssrc/<framework>/provides the safe Rust API on top
The only runtime Rust dependency is doom-fish-utils.
Ownership and mapped-memory contracts
Raw from_raw constructors adopt one caller-owned +1 retain and are therefore unsafe. Use from_raw_borrowed when importing a live +0 pointer that the wrapper must retain. AsCFType is an unsafe trait because implementations promise a valid Core Foundation object pointer.
CVPixelBuffer and IOSurface lock guards balance native synchronization and mapping only; they do not establish Rust exclusivity across retained, native, GPU, or cross-process aliases. Raw pointers remain available, while slice, row, plane, and zero-copy cursor views require unsafe with an explicit no-alias/no-mutation guarantee. CGContext clones likewise share one mutable native context, so its byte-slice views are unsafe even though drawing methods remain safe.
CMBlockBuffer::as_slice and CMBlockBuffer::cursor_ref are unsafe for the same reason: the bytes belong to Core Media, can be shared with other sample buffers, and must be initialized and left unmodified while the slice lives. CMBlockBuffer::cursor and copy_data_bytes copy instead. CMSampleBuffer::audio_buffer_list hands out read-only views that keep the backing block buffer alive.
Constructors that can fail on ordinary input return Option (CFURL::from_string, CFURL::from_file_system_path, CFTimeZone::new, CFCalendar::new, CFMessagePort::create_echo_local, DispatchSemaphore::new), and CFString::new accepts interior NUL bytes. CFURL::from_string follows the running system's CFURLCreateWithString, whose rules changed in macOS 27: it rejects an authority with an unbalanced or misplaced [ or ] or a second @ (http://[bad, http://bad], http://user@@host/), which earlier releases accept unchanged. CFFileDescriptor takes an OwnedFd, which Core Foundation closes, or a BorrowedFd, which it never closes.
CVPixelBufferPool::create(..., max_buffers) enforces the cap through Core Video's per-allocation threshold. Clones share that immutable policy across threads. Per-call auxiliary attributes are honored, flush flags map directly to the native API, and try_create_pixel_buffer distinguishes threshold exhaustion from other errors.
Examples and tests
This release ships 15 numbered examples plus dedicated smoke tests for:
- CoreFoundation primitives, collections, property lists, resources, runtime helpers
- Dispatch serial, concurrent, main and global queues,
dispatch_async,dispatch_async_and_wait,dispatch_apply,dispatch_after, groups, semaphores, and timer sources CMTimeRange,CMTimebase,CMMetadataFormatDescription, and low-levelCMTag/CMSynccoverage throughapple_cf::rawCVBuffer,CVImageBuffer,CVPixelBuffer,CVMetalTextureCache, and the remaining CVMetal entry points throughapple_cf::raw- Exhaustive low-level constants / inline helpers surfaced by
apple_cf::raw
CVDisplayLink remains exempt in the audit because Apple deprecated the family on macOS 15.
Not wrapped yet
These are reachable only through the unsafe declarations in apple_cf::raw:
CMSampleBuffercreation, timing-info arrays,CMSampleBufferCopyPCMDataIntoAudioBufferListand the other audio-buffer-list constructors- Run-loop sources and observers,
CFRunLoopTimer/CFTimerwith a user callback, and socket, file-descriptor and stream callbacks - Dispatch sources with user event handlers, dispatch work items and dispatch I/O
- IOSurface lookup by ID, Mach port or XPC object, and Metal texture creation from
CVMetalTextureCache CFTreeRef:cf::CFTreeis a Swift-side tree of retained values, not a Core Foundation tree
License
Licensed under either of Apache-2.0 or MIT at your option.