This library provides safe Rust bindings for AppKit
on macOS (beta quality, fairly usable) and UIKit
on iOS/tvOS (alpha quality, see repo).
It tries to do so in a way that, if you’ve done programming for the framework before (in Swift or
Objective-C), will feel familiar. This is tricky in Rust due to the ownership model, but some
creative coding and assumptions can get us pretty far.
This exists on crates.io in part to enable the project to see wider usage, which can
inform development. That said, this library is currently early stages and may have bugs – your usage of it is at
your own risk. However, provided you follow the rules (regarding memory/ownership) it’s
already fine for some apps. The core repository has a wealth of examples to help you get started.
Important
If you are migrating from 0.2 to 0.3, you should elect either
appkit
oruikit
as a feature in yourCargo.toml
. This change was made to
support platforms that aren’t just macOS/iOS/tvOS (e.g, gnustep, airyx). One of these features is required to work;appkit
is defaulted for
ease of development.
Note that this crate relies on the Objective-C runtime. Interfacing with the runtime requires
unsafe blocks; this crate handles those unsafe interactions for you and provides a safe wrapper,
but by using this crate you understand that usage ofunsafe
is a given and will be somewhat
rampant for wrapped controls. This does not mean you can’t assess, review, or question unsafe
usage – just know it’s happening, and in large part it’s not going away. Issues pertaining to the mere
existence of unsafe will be closed without comment.
If you’re looking to build the docs for this on your local machine, you’ll want the following due to the way feature flags work
with cargo doc
:
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --open
use cacao::appkit::{App, AppDelegate}; use cacao::appkit::window::Window; #[derive(Default)] struct BasicApp { window: Window } impl AppDelegate for BasicApp { fn did_finish_launching(&self) { self.window.set_minimum_content_size(400., 400.); self.window.set_title("Hello World!"); self.window.show(); } } fn main() { App::new("com.hello.world", BasicApp::default()).run(); }
For more thorough examples, check the examples/
folder.
If you’re interested in a more “kitchen sink” example, check out the todos_list with:
cargo run --example todos_list
Initialization
Due to the way that AppKit and UIKit programs typically work, you’re encouraged to do the bulk
of your work starting from the did_finish_launching()
method of your AppDelegate
. This
ensures the application has had time to initialize and do any housekeeping necessary behind the
scenes.
Currently Supported
In terms of mostly working pieces, the table below showcases the level of support for varying features. This list is not exhaustive just by virtue of documentation updating being hell – so you’re encouraged to check out the code-built documentation for more info:
Note that while iOS has green checkmarks, some components still aren’t as well defined (e.g, Views/ViewControllers are still very alpha there).
Non-Apple platforms that shim or provide a form of AppKit may be able to use a good chunk of the AppKit support in this library.
Component | Description | AppKit | iOS | tvOS |
---|---|---|---|---|
App | Initialization & events | |||
Window | Construction, handling, events | |||
View | Construction, styling, events | |||
ViewController | Construction, lifecycle events | |||
Color | System-backed colors, theming | |||
ListView | Reusable list w/ cached rows | |||
Button | Styling, events, toolbar support | |||
Label/TextField | Text rendering & input | |||
Image/ImageView | Loading, drawing, etc | |||
Toolbar | Basic native toolbar | |||
SplitViewController | Split views (Big Sur friendly) | |||
WebView | Wrapper for WKWebView | |||
UserDefaults | Persisting small data | |||
Autolayout | View layout for varying screens |
Optional Features
The following are a list of Cargo features that can be enabled or disabled.
appkit
: LinksAppKit.framework
.uikit
: LinksUIKit.framework
(iOS/tvOS only).cloudkit
: LinksCloudKit.framework
and provides some wrappers around CloudKit functionality. Currently not feature complete.color_fallbacks
: Provides fallback colors for older systems wheresystemColor
types don’t exist. This feature is very uncommon and you probably don’t need it.quicklook
: LinksQuickLook.framework
and offers methods for generating preview images for files.user-notifications
: LinksUserNotifications.framework
and provides functionality for emitting notifications on macOS and iOS. Note that this requires your application be code-signed, and will not work without it.webview
: LinksWebKit.framework
and provides aWebView
control backed byWKWebView
. This feature is not supported on tvOS, as the platform has no webview control. This feature is also potentially only supported for macOS/iOS due to the WKWebView control and varying support on non-Apple platforms.webview-downloading-macos
: Enables downloading files from theWebView
via a private interface. This is not an App-Store-safe feature, so be aware of that before enabling. This feature is not supported on iOS (a user would handle downloads very differently) or tvOS (there’s no web browser there at all).
General Notes
Why not extend the existing cocoa-rs crate?
A good question. At the end of the day, that crate (I believe, and someone can correct me if I’m wrong) is somewhat tied to Servo, and I wanted to experiment with what the best approach for representing the Cocoa UI model in Rust was. This crate doesn’t ignore their work entirely, either – core_foundation
and core_graphics
are used internally and re-exported for general use.
Why should I write in Rust, rather than X language?
In my case, I want to be able to write native applications for my devices (and the platform I like to build products for) without being locked in to writing in Apple-specific languages… and without writing in C/C++ or JavaScript (note: the toolchain, not the language – ES6/Typescript are fine). I want to do this because I’m tired of hitti