• Stars
    star
    1,560
  • Rank 29,997 (Top 0.6 %)
  • Language
    Rust
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Rust bindings for AppKit (macOS) and UIKit (iOS/tvOS). Experimental, but working!

Cacao

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 or uikit as a feature in your Cargo.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 of unsafe 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

Hello World

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: Links AppKit.framework.
  • uikit: Links UIKit.framework (iOS/tvOS only).
  • cloudkit: Links CloudKit.framework and provides some wrappers around CloudKit functionality. Currently not feature complete.
  • color_fallbacks: Provides fallback colors for older systems where systemColor types don't exist. This feature is very uncommon and you probably don't need it.
  • quicklook: Links QuickLook.framework and offers methods for generating preview images for files.
  • user-notifications: Links UserNotifications.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: Links WebKit.framework and provides a WebView control backed by WKWebView. 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 the WebView 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 hitting a mountain of work when I want to port my applications to other ecosystems. I think that Rust offers a (growing, but significant) viable model for sharing code across platforms and ecosystems without sacrificing performance.

(This is the part where the internet lights up and rants about some combination of Electron, Qt, and so on - we're not bothering here as it's beaten to death elsewhere)

This crate is useful for people who don't need to go all-in on the Apple ecosystem, but want to port their work there with some relative ease. It's not expected that everyone will suddenly want to rewrite their macOS/iOS/tvOS apps in Rust.

Isn't Objective-C dead?
Yes, and no.

It's true that Apple definitely favors Swift, and for good reason (and I say this as an unabashed lover of Objective-C). With that said, I would be surprised if we didn't have another ~5+ years of support; Apple is quick to deprecate, but removing the Objective-C runtime would require a ton of time and effort. Maybe SwiftUI kills it, who knows. A wrapper around this stuff should conceivably make it easier to swap out the underlying UI backend whenever it comes time.

One thing to note is that Apple has started releasing Swift-only frameworks. For cases where you need those, it should be possible to do some combination of linking and bridging - which would inform how swapping out the underlying UI backend would happen at some point.

Some might also decry Objective-C as slow. To that, I'd note the following:

  • Your UI engine is probably not the bottleneck.
  • Swift is generally better as it fixes a class of bugs that Objective-C doesn't catch; for the most part it still sits on top of the existing Cocoa frameworks anyway (though this statement will not age well~).
  • Message dispatching in Objective-C is more optimized than significant chunks of the code you'll write, and is fast enough for most things.

tl;dr it's probably fine, and you have Rust for your performance needs.

Why not just wrap UIKit, and then rely on Catalyst?
I have yet to see a single application where Catalyst felt good. The goal is good, though, and if it got to a point where that just seemed like the way forward (e.g, Apple just kills AppKit) then it's certainly an option.

You can't possibly wrap all platform-specific behavior here...
Correct! Each UI control contains a objc field, which you can use as an escape hatch - if the control doesn't support something, you're free to drop to the Objective-C runtime yourself and handle it.

Why don't you use bindings to automatically generate this stuff?
For initial exploration purposes I've done most of this by hand, as I wanted to find an approach that fit well in the Rust model before committing to binding generation. This is something I'll likely focus on next now that I've got things "working" well enough.

Is this related to Cacao, the Swift project?
No. The project referred to in this question aimed to map portions of Cocoa and UIKit over to run on Linux, but hasn't seen activity in some time (it was really cool, too!).

Open source project naming in 2020 is like trying to buy a .com domain: everything good is taken. Luckily, multiple projects can share a name... so that's what's going to happen here.

Isn't this kind of cheating the Rust object model?
Depends on how you look at it. I personally don't care too much - the GUI layer for these platforms is a hard requirement to support for certain classes of products, and giving them up also means giving up battle-tested tools for things like Accessibility and deeper OS integration. With that said, internally there are efforts to try and make things respect Rust's model of how things should work.

You can think of this as similar to gtk-rs. If you want to support or try a more pure model, go check out Druid or something. :)

License

Dual licensed under an MIT/MPL-2.0 license. See the appropriate files in this repository for more information. Apple, AppKit, UIKit, Cocoa, and other trademarks are copyright Apple, Inc.

Questions, Comments, etc

You can follow me over on twitter or email me with questions that don't fit as an issue here.

More Repositories

1

twython

Actively maintained, pure Python wrapper for the Twitter API. Supports both normal and streaming Twitter APIs.
Python
1,849
star
2

wrench-js

Recursive file operations in Node.js
JavaScript
438
star
3

alchemy

An experimental GUI framework for Rust, backed by per-platform native widgets. React, AppKit/UIKit inspired. EXPERIMENTAL, runs on Cocoa right now. ;P
Rust
384
star
4

wii-js

A sane, documented, (hopefully) performant event-based library for Wiimote webpage interaction.
JavaScript
154
star
5

jelly

User authentication/sessions/etc for Actix-Web. More of a sample project than a crate, but probably useful to some people.
Rust
89
star
6

twython-django

An example Django application to showcase how to use OAuth with Twitter in Django using Twython.
Python
73
star
7

1.1.1.1-macOS

Experimenting with cloning CloudFlare's 1.1.1.1 app as a macOS status bar app.
Swift
34
star
8

cloudkit-sane-sharing

A dump of code that illustrates a better way to share CloudKit resources.
Swift
29
star
9

react-iconpack

A React Component for handling SVG icons, coupled with Babel and Browserify plugins to only bundle the icons you use.
JavaScript
25
star
10

django-rednoise

An opinionated addon for WhiteNoise, with a focus on Django environments.
Python
18
star
11

pythentic_jobs

A pure Python wrapper around the Authentic Jobs (http://www.authenticjobs.com) API.
Python
18
star
12

svgalib-1

It's not svgalib "dash" 1, it's svgalib negative 1, because if you still use this library there's probably something wrong with you. That said, this is a 'fork' of the most recent (haha) version found on the internets, with a ton of patches from various people around the internet cobbled together. I have not and will not make any outlandish efforts to credit people, but if you see something here you wrote and you want credit, message me. Should fix a lot of compiling issues under recent issues of Linux.
C
16
star
13

webpack-babel-react-setup-lesson

Walking through setting up Webpack, Babel, and React.
JavaScript
15
star
14

memelee

An unofficial smash.gg app, read-only.
JavaScript
13
star
15

node-utf8

utf8 encoding and decoding in Node.js
JavaScript
12
star
16

drinkkitcom

A Foursquare clone written in Django to let Redditors broadcast DC bar crawls.
Python
11
star
17

gitstatus

A Github-repository widget to display most recent commits on a given repository.
JavaScript
8
star
18

jTransliterate

Transliterate [Hirag/Katak]ana to Latin/English and back with Python. Convert half/full-width Japanese text.
Python
7
star
19

holidaycalendar

A demo holiday calendar, which shows how to make NSCollectionViewItem's swipeable.
Swift
6
star
20

franz

Client side color swatches.
JavaScript
4
star
21

beyond-react

Session 4, going beyond React and integrating with other libraries.
JavaScript
3
star
22

activity-scraper

A social media scraper written in Rust. Used for the data on my personal site.
Rust
3
star
23

shinekit

iOS/macOS/Windows UI in Rust. Highly experimental.
Rust
3
star
24

rubeclosures

Ruby wrapper for the foreclosurelistings.com API
Ruby
2
star
25

splash

A programming language aimed at kids. Builds on their existing writing skills and doesn't try to reinvent arcane logic.
JavaScript
2
star
26

react-flux-redux-lesson

Walkthrough guide for React session #2 via Codementor.
JavaScript
2
star
27

react-router-lesson

React Router & co.
JavaScript
1
star
28

jsmag

Random JSMag Code Samplings
JavaScript
1
star
29

katakana

An Android application that aims to teach people Katakana through a basic brain-timing-calculation/algorithmic method.
Java
1
star
30

redditimages

Reddit: Images in Comments hack
JavaScript
1
star
31

takeoff

A Chrome extension to randomly load up one of your bookmarks on new windows/tabs, built for user "hokku". Enjoy.
1
star
32

smashgg-upcoming-tournaments

A scraper example for finding upcoming tournaments on smash.gg. From an unreleased side project, might be fun for some people.
Rust
1
star