Expand description
§Objective-C interface and runtime bindings
Quick links:
Objective-C was the standard programming language on Apple platforms like macOS, iOS, iPadOS, tvOS and watchOS. It is an object-oriented language centered around “sending messages” to its instances - this can for the most part be viewed as a function call.
It has since been superseded by Swift, but most of the core libraries and frameworks that are in use on Apple systems are still written in Objective-C, and hence we would like the ability to interact with these using Rust. This crate enables bi-directional interop with Objective-C, in as safe a manner as possible.
§Example
Most of the time, you’ll want to use one of the framework crates, which
contain bindings to CoreFoundation
, Foundation
, AppKit
, Metal
,
UIKit
, WebKit
and so on.
In this example we’re going to be using objc2-foundation
and
objc2-app-kit
to create a simple GUI application that displays a
“Hello World” label.
$ # Add the necessary crates to your project.
$ cargo add objc2 objc2-foundation objc2-app-kit
#![deny(unsafe_op_in_unsafe_fn)]
use std::cell::OnceCell;
use objc2::rc::Retained;
use objc2::runtime::ProtocolObject;
use objc2::{define_class, msg_send, DefinedClass, MainThreadOnly};
use objc2_app_kit::{
NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate, NSAutoresizingMaskOptions,
NSBackingStoreType, NSColor, NSFont, NSTextAlignment, NSTextField, NSWindow, NSWindowDelegate,
NSWindowStyleMask,
};
use objc2_foundation::{
ns_string, MainThreadMarker, NSNotification, NSObject, NSObjectProtocol, NSPoint, NSRect,
NSSize,
};
#[derive(Debug, Default)]
struct AppDelegateIvars {
window: OnceCell<Retained<NSWindow>>,
}
define_class!(
// SAFETY:
// - The superclass NSObject does not have any subclassing requirements.
// - `Delegate` does not implement `Drop`.
#[unsafe(super = NSObject)]
#[thread_kind = MainThreadOnly]
#[name = "Delegate"]
#[ivars = AppDelegateIvars]
struct Delegate;
// SAFETY: `NSObjectProtocol` has no safety requirements.
unsafe impl NSObjectProtocol for Delegate {}
// SAFETY: `NSApplicationDelegate` has no safety requirements.
unsafe impl NSApplicationDelegate for Delegate {
// SAFETY: The signature is correct.
#[unsafe(method(applicationDidFinishLaunching:))]
fn did_finish_launching(&self, notification: &NSNotification) {
let mtm = self.mtm();
let app = unsafe { notification.object() }
.unwrap()
.downcast::<NSApplication>()
.unwrap();
let text_field = unsafe {
let text_field = NSTextField::labelWithString(ns_string!("Hello, World!"), mtm);
text_field.setFrame(NSRect::new(
NSPoint::new(5.0, 100.0),
NSSize::new(290.0, 100.0),
));
text_field.setTextColor(Some(&NSColor::colorWithSRGBRed_green_blue_alpha(
0.0, 0.5, 0.0, 1.0,
)));
text_field.setAlignment(NSTextAlignment::Center);
text_field.setFont(Some(&NSFont::systemFontOfSize(45.0)));
text_field.setAutoresizingMask(
NSAutoresizingMaskOptions::ViewWidthSizable
| NSAutoresizingMaskOptions::ViewHeightSizable,
);
text_field
};
// SAFETY: We disable releasing when closed below.
let window = unsafe {
NSWindow::initWithContentRect_styleMask_backing_defer(
NSWindow::alloc(mtm),
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(300.0, 300.0)),
NSWindowStyleMask::Titled
| NSWindowStyleMask::Closable
| NSWindowStyleMask::Miniaturizable
| NSWindowStyleMask::Resizable,
NSBackingStoreType::Buffered,
false,
)
};
// SAFETY: Disable auto-release when closing windows.
// This is required when creating `NSWindow` outside a window
// controller.
unsafe { window.setReleasedWhenClosed(false) };
// Set various window properties.
window.setTitle(ns_string!("A window"));
let view = window.contentView().expect("window must have content view");
unsafe { view.addSubview(&text_field) };
window.center();
unsafe { window.setContentMinSize(NSSize::new(300.0, 300.0)) };
window.setDelegate(Some(ProtocolObject::from_ref(self)));
// Show the window.
window.makeKeyAndOrderFront(None);
// Store the window in the delegate.
self.ivars().window.set(window).unwrap();
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
// Activate the application.
// Required when launching unbundled (as is done with Cargo).
#[allow(deprecated)]
app.activateIgnoringOtherApps(true);
}
}
// SAFETY: `NSWindowDelegate` has no safety requirements.
unsafe impl NSWindowDelegate for Delegate {
#[unsafe(method(windowWillClose:))]
fn window_will_close(&self, _notification: &NSNotification) {
// Quit the application when the window is closed.
unsafe { NSApplication::sharedApplication(self.mtm()).terminate(None) };
}
}
);
impl Delegate {
fn new(mtm: MainThreadMarker) -> Retained<Self> {
let this = Self::alloc(mtm).set_ivars(AppDelegateIvars::default());
// SAFETY: The signature of `NSObject`'s `init` method is correct.
unsafe { msg_send![super(this), init] }
}
}
fn main() {
let mtm = MainThreadMarker::new().unwrap();
let app = NSApplication::sharedApplication(mtm);
let delegate = Delegate::new(mtm);
app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
app.run();
}
§Crate features
This crate exports several optional cargo features, see Cargo.toml
for
an overview and description of these.
The features in the framework crates are described here. Note that if you’re developing a library for others to use, you might want to reduce compile times by disabling default features and only enabling the features you need.
§Supported operating systems
- macOS:
10.12-15.2
- iOS:
10.0-18.2
(includes iPadOS and Mac Catalyst) - tvOS:
10.0-18.2
- watchOS:
5.0-11.2
- visionOS:
1.0-2.2
The minimum versions are the same as those supported by rustc
. Higher
versions will also work, but the framework crates will not have bindings
available for newer APIs.
The framework bindings are generated from the SDKs in Xcode 16.2. The Xcode version will be periodically updated.
Note that the bindings are currently generated in a very macOS-centric, so they may try to use types from AppKit, even on iOS, see for example #637.
The bindings can also be used on Linux or *BSD utilizing the
GNUstep Objective-C runtime, see
the ffi
module for how to configure this, but this is very much
second-class.
§Minimum Supported Rust Version (MSRV)
The currently minimum supported Rust version is 1.71
(to be able to
use extern "C-unwind"
functions); this is not defined by policy,
though, so it may change in at any time in a patch release.
Help us define a policy over in #203.
Re-exports§
pub use self::encode::Encode;
pub use self::encode::Encoding;
pub use self::encode::RefEncode;
pub use DefinedClass as DeclaredClass;
Modules§
- declare
Deprecated - Deprecated location for a few things that are now in the
runtime
module. - encode
- Support for type-encodings.
- exception
@throw
and@try/@catch
exceptions.- ffi
- Raw bindings to Objective-C runtimes
- rc
- Reference counting utilities.
- runtime
- Direct runtime bindings.
- topics
docsrs
- Various explanations and topics of discussion.
Macros§
- available
- Check if APIs from the given operating system versions are available.
- class
- Gets a reference to an
AnyClass
from the given name. - define_
class - Create a new Objective-C class.
- extern_
class - Create a new type to represent a class.
- extern_
methods - Define methods on an external class.
- extern_
protocol - Create a new trait to represent a protocol.
- msg_
send - Send a message to an object or class.
- msg_
send_ bool Deprecated - Use
msg_send!
instead, it now supports converting to/frombool
. - msg_
send_ id Deprecated - Use
msg_send!
instead, it now supports converting to/fromRetained
. - sel
- Register a selector with the Objective-C runtime.
Structs§
- Main
Thread Marker - A marker type taken by functions that can only be executed on the main thread.
Traits§
- Alloc
AnyThread - Marker trait for classes (and protocols) that are usable from any thread,
i.e. the opposite of
MainThreadOnly
. - Class
Type - Marks types that represent specific classes.
- Defined
Class - Marks class types whose implementation is defined in Rust.
- Downcast
Target - Classes that can be safely downcasted to.
- Main
Thread Only - Marker trait for classes and protocols that are only safe to use on the main thread.
- Message
- Types that can be sent Objective-C messages.
- Protocol
Type - Marks types that represent specific protocols.
- Thread
Kind - The allowed values in
ClassType::ThreadKind
.