{"id":13477850,"url":"https://github.com/nvzqz/fruity","last_synced_at":"2025-04-05T05:08:15.021Z","repository":{"id":41154242,"uuid":"293691564","full_name":"nvzqz/fruity","owner":"nvzqz","description":"Rusty bindings for Apple libraries","archived":false,"fork":false,"pushed_at":"2022-08-29T12:36:25.000Z","size":326,"stargazers_count":175,"open_issues_count":11,"forks_count":8,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-03-29T04:09:13.196Z","etag":null,"topics":["api","appkit","apple","bindings","cf","cocoa","ffi","foundation","graphics","ns","objc","rust","system","uikit"],"latest_commit_sha":null,"homepage":"https://docs.rs/fruity","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nvzqz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["nvzqz"],"custom":["https://www.paypal.me/nvzqz"]}},"created_at":"2020-09-08T03:30:54.000Z","updated_at":"2025-03-25T14:17:35.000Z","dependencies_parsed_at":"2022-09-05T14:01:26.108Z","dependency_job_id":null,"html_url":"https://github.com/nvzqz/fruity","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Ffruity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Ffruity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Ffruity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nvzqz%2Ffruity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nvzqz","download_url":"https://codeload.github.com/nvzqz/fruity/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247289428,"owners_count":20914464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","appkit","apple","bindings","cf","cocoa","ffi","foundation","graphics","ns","objc","rust","system","uikit"],"created_at":"2024-07-31T16:01:48.487Z","updated_at":"2025-04-05T05:08:15.002Z","avatar_url":"https://github.com/nvzqz.png","language":"Rust","funding_links":["https://github.com/sponsors/nvzqz","https://www.paypal.me/nvzqz","https://www.paypal.me/nvzqz)!"],"categories":["Rust"],"sub_categories":[],"readme":"# Fruity\n\n[\u003cimg alt=\"github\" src=\"https://img.shields.io/badge/github-nvzqz/fruity-8da0cb?style=for-the-badge\u0026labelColor=555555\u0026logo=github\" height=\"24\"\u003e](https://github.com/nvzqz/fruity)\n[\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/fruity.svg?style=for-the-badge\u0026color=fc8d62\u0026logo=rust\" height=\"24\"\u003e](https://crates.io/crates/fruity)\n[\u003cimg alt=\"docs.rs\" src=\"https://img.shields.io/badge/docs.rs-fruity-66c2a5?style=for-the-badge\u0026labelColor=555555\u0026logoColor=white\u0026logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K\" height=\"24\"\u003e](https://docs.rs/fruity)\n\nRusty bindings for Apple libraries, brought to you by\n[@NikolaiVazquez](https://twitter.com/NikolaiVazquez).\n\n## Index\n\n1. [Donate](#donate)\n2. [Usage](#usage)\n   1. [Feature Flags](#feature-flags)\n3. [Goals](#goals)\n   1. [Idiomatic Rust](#idiomatic-rust)\n   2. [Zero Cost](#zero-cost)\n4. [License](#license)\n\n## Donate\n\nIf this project is useful to you, consider\n[sponsoring me](https://github.com/sponsors/nvzqz) or\n[donating directly](https://www.paypal.me/nvzqz)!\n\nDoing so enables me to create high-quality open source software like this. ❤️\n\n## Usage\n\nThis library is available [on crates.io][crate] and can be used in your project\nby adding the following to your project's [`Cargo.toml`]:\n\n```toml\n[dependencies.fruity]\nversion = \"0.3.0\"\n```\n\n### Feature Flags\n\nEach module for a library or framework has its own\n[feature flag](https://doc.rust-lang.org/cargo/reference/features.html)\nwith the same name.\n\nFor example, this is how you enable the\n[`foundation`](https://docs.rs/fruity/0.3.0/fruity/foundation/index.html)\nmodule:\n\n```toml\n[dependencies.fruity]\nversion = \"0.3.0\"\nfeatures = [\"foundation\"]\n```\n\nThis feature transitively enables the\n[`objc`](https://docs.rs/fruity/0.3.0/fruity/objc/index.html)\nfeature/module.\n\n## Goals\n\n### Idiomatic Rust\n\nFruity makes interfacing with these C and Objective-C APIs feel natural in Rust.\n\n- **Automatic Reference Counting.**\n\n  Fruity takes advantage of Rust's\n  [ownership model](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html)\n  to handle object reference counting for you.\n\n  [`NSObject`](https://docs.rs/fruity/0.3.0/fruity/objc/struct.NSObject.html)\n  is a smart pointer that calls\n  [`retain`](https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571946-retain)\n  on [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html) and\n  [`release`](https://developer.apple.com/documentation/objectivec/1418956-nsobject/1571957-release)\n  on [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html). This is\n  exactly how Rust's\n  [`Arc\u003cT\u003e`](https://doc.rust-lang.org/std/sync/struct.Arc.html) works.\n\n- **`Option\u003cNSObject\u003e`.**\n\n  In Objective-C, all objects are nullable unless marked with `_Nonnull`. This\n  often leads to either very defensive checks or careless ignoring of null\n  objects.\n\n  Fruity reverses that and instead makes all objects (such as\n  [`NSObject`](https://docs.rs/fruity/0.3.0/fruity/objc/struct.NSObject.html))\n  non-null by default. An object can be made nullable by wrapping it with\n  [`Option\u003cT\u003e`](https://doc.rust-lang.org/std/option/enum.Option.html).\n\n  To make FFI safe and easy, the following Objective-C and Rust types are\n  ABI-compatible:\n\n  - `NSObject * _Nonnull` and `NSObject`\n\n  - `NSObject * _Nullable` and `Option\u003cNSObject\u003e`\n\n  This is because\n  [`NSObject`](https://docs.rs/fruity/0.3.0/fruity/objc/struct.NSObject.html)\n  is a\n  [`#[repr(transparent)]`](https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent)\n  wrapper around a\n  [`NonNull\u003cT\u003e`](https://doc.rust-lang.org/std/ptr/struct.NonNull.html)\n  pointer.\n\n- **`Result\u003cT, NSError\u003e`.**\n\n  In Objective-C, methods take a pointer to where an\n  [`NSError`](https://developer.apple.com/documentation/foundation/nserror)\n  is placed upon failure. This makes it easy to avoid error handling and assume\n  the happy path, which can lead to bugs when errors occur.\n\n  Fruity instead returns a\n  [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html), which\n  is the canonical way to handle errors in Rust. This ensures that errors must\n  be acknowledged in some way.\n\n- **Natural inheritance.**\n\n  Most of these types are classes that inherit from each other. Because true\n  inheritance is not possible in Rust, Fruity uses\n  [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html)\n  to model Objective-C subclassing.\n\n- **Builder Pattern.**\n\n  Types like\n  [`DispatchQueue`](https://docs.rs/fruity/0.3.0/fruity/dispatch/struct.DispatchQueue.html)\n  have many configurable inputs to create an instance. Many of these inputs have\n  standard default values, so it is cumbersome to specify them all each time.\n  Swift solves this by having default parameters in\n  [`init`](https://developer.apple.com/documentation/dispatch/dispatchqueue/2300059-init).\n  However, Rust does not have default function parameters.\n\n  Fruity instead solves this using the\n  [builder pattern](https://doc.rust-lang.org/1.0.0/style/ownership/builders.html).\n  See\n  [`DispatchQueueBuilder`](https://docs.rs/fruity/0.3.0/fruity/dispatch/struct.DispatchQueueBuilder.html)\n  as an example. This reduces and simplifies code for creating dispatch queues.\n\n### Zero Cost\n\nUsing Fruity to interface with Objective-C libraries should have as little\nruntime cost as writing the same code directly in Objective-C.\n\nThis is true for the following:\n\n- **Calling object methods.**\n\n  Method dispatch is always direct and does not need the error checking overhead\n  of other wrappers that use the\n  [`objc::msg_send!`](https://docs.rs/objc/0.2.*/objc/macro.msg_send.html)\n  macro. This also reduces the size of your program by not emitting panics that\n  would otherwise never get called.\n\n  This library is carefully written to ensure that calls to\n  [`objc_msgSend`](https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend)\n  are always done with the correct object type, method selector, and arguments.\n\n- **Getting a static class.**\n\n  Getters like `NSString::class` retrieve the class directly through its symbol.\n  This is instantaneous, especially when compared to calling into the\n  Objective-C runtime via\n  [`objc_getClass`](https://developer.apple.com/documentation/objectivec/1418952-objc_getclass).\n\n- **Creating an `NSString` from a Rust string literal.**\n\n  The [`nsstring!`](https://docs.rs/fruity/0.3.0/fruity/macro.nsstring.html)\n  macro creates an `NSString` literal (i.e. `@\"string\"`) at compile time. There\n  is no runtime dispatch/allocation/initialization cost.\n\nSome parts of this library still aren't zero cost. Your help would be much\nappreciated here!\n\nThese are:\n\n- **The `selector!` macro.** See\n  [issue #2](https://github.com/nvzqz/fruity/issues/2)\n  for details.\n\n## License\n\nThis project is released under either the\n[MIT License](https://github.com/nvzqz/fruity/blob/main/LICENSE-MIT) or\n[Apache License (Version 2.0)](https://github.com/nvzqz/fruity/blob/main/LICENSE-APACHE),\nat your choosing.\n\n[crate]: https://crates.io/crates/fruity\n[`Cargo.toml`]: https://doc.rust-lang.org/cargo/reference/manifest.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnvzqz%2Ffruity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnvzqz%2Ffruity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnvzqz%2Ffruity/lists"}