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, active displays and display modes | cg |
✅ |
iosurface |
IOSurface (zero-copy GPU buffers, multi-planar formats) | iosurface |
✅ |
dispatch_queue |
Dispatch queues, async/apply helpers, groups, semaphores, timer and data-add 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 12.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!;
Read text boundaries
CFString::composed_character_range returns the composed-character range at a valid UTF-16 index.
CFString::composed_character_ranges iterates the composed-character ranges.
CFStringTokenizer::new accepts a string, a CFStringTokenUnit, and an optional locale.
The tokenizer borrows the string and releases its native handle when it drops.
Word returns word tokens. WordBoundary also returns separator ranges.
All ranges use UTF-16 code units, not UTF-8 bytes.
Invalid indexes give None. Every native range has a length and a checked end within the source string.
The system supplies Unicode data and language-specific word rules.
CFCharacterSet::predefined supplies decimal-digit and non-base character sets.
Read URL components
CFURL::parse accepts UTF-8 input and an optional base URL.
A base URL gives an absolute URL.
CFURL::component returns encoded component text without percent decoding.
An absent component gives None.
An empty component gives an empty string.
use ;
let base = CFURLparse.unwrap;
let page = CFURLparse.unwrap;
assert_eq!;
assert_eq!;
The system parser can reject input on a newer OS. The application must keep its own input and normalization rules.
Create a timer-backed dispatch source
use DispatchSource;
use thread;
use Duration;
let source = timer;
source.resume;
sleep;
source.cancel;
assert!;
DispatchDataAddSource::new makes a data-add source on the main queue. Call it on the main thread, then call resume.
Send a DispatchDataAddMergeHandle to other threads to merge data. The handler runs on the main queue.
Drop or cancel the source to stop it. Merge handles remain valid after cancellation.
Read the active displays
use Display;
for display in active
Display::main gives the main display. Display::uuid returns its UUID if one exists.
DisplayMode gives the logical size, pixel size and refresh rate of the current mode.
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
CGContext::new_srgb_rgba8 creates an sRGB bitmap with premultiplied RGBA bytes and exact row sizes.
The unsafe copy_bytes method copies pixels into owned storage.
The caller must ensure that all bytes have values and that no native alias changes them during the copy.
The sRGB constructor initializes all bytes.
Quadratic and cubic paths support typed caps, joins, miter limits, fill rules, clipping, and affine transforms.
These operations support macOS 12.
Use a separate context for each concurrent operation.
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.