Crate objc2_foundation

source ·
Expand description

§Bindings to the Foundation framework

See Apple’s docs and the general docs on framework crates for more information.

This is the std equivalent for Objective-C, containing essential data types, collections, and operating-system services.

§Rust vs. Objective-C types

A quick overview of some types you will encounter often in Objective-C, and their approximate Rust equivalent.

Objective-C(approximately) equivalent Rust
NSData*Arc<[u8]>
NSMutableData*Vec<u8>
NSString*Arc<str>
NSMutableString*String
NSValue*Arc<dyn Any>
NSNumber*Arc<enum { I8(i8), U8(u8), I16(i16), U16(u16), I32(i32), U32(u32), I64(i64), U64(u64), F32(f32), F64(f64), CLong(ffi::c_long), CULong(ffi::c_ulong) }>
NSError*Arc<dyn Error + Send + Sync>
NSException*Arc<dyn Error + Send + Sync>
NSRangeops::Range<usize>
NSComparisonResultcmp::Ordering
NSArray<T>*Arc<[T]>
NSMutableArray<T>*Vec<T>
NSDictionary<K, V>*Arc<HashMap<K, V>>
NSMutableDictionary<K, V>*HashMap<K, V>
NSEnumerator<T>*Box<dyn Iterator<T>>
NSCopying*Box<dyn Clone>

§Examples

Basic usage of a few Foundation types.

$ cargo add objc2-foundation --features=all
use objc2_foundation::{ns_string, NSCopying, NSArray};

let string = ns_string!("world");
println!("hello {string}");

let array = NSArray::from_id_slice(&[string.copy()]);
println!("{array:?}");
use objc2::rc::autoreleasepool;
use objc2_foundation::{ns_string, NSArray, NSDictionary, NSObject};

fn main() {
    // Create and compare NSObjects
    let obj = NSObject::new();
    #[allow(clippy::eq_op)]
    {
        println!("{obj:?} == {obj:?}? {:?}", obj == obj);
    }

    let obj2 = NSObject::new();
    println!("{obj:?} == {obj2:?}? {:?}", obj == obj2);

    // Create an NSArray from a Vec
    let objs = vec![obj, obj2];
    let array = NSArray::from_vec(objs);
    for obj in array.iter() {
        println!("{obj:?}");
    }
    println!("{}", array.len());

    // Turn the NSArray back into a Vec
    let mut objs = array.to_vec_retained();
    let obj = objs.pop().unwrap();

    // Create a static NSString
    let string = ns_string!("Hello, world!");
    // Use an autoreleasepool to get the `str` contents of the NSString
    autoreleasepool(|pool| {
        println!("{}", string.as_str(pool));
    });
    // Or use the `Display` implementation
    let _s = string.to_string(); // Using ToString
    println!("{string}"); // Or Display directly

    // Create a dictionary mapping strings to objects
    let keys = &[string];
    let objects = &[obj];
    let dict = NSDictionary::from_id_slice(keys, objects);
    println!("{:?}", dict.get(string));
    println!("{}", dict.len());
}

An example showing how to define your own interfaces to parts that may be missing in the autogenerated interface.

//! Speak synthethized text.
//!
//! This uses `NSSpeechSynthesizer` on macOS, and `AVSpeechSynthesizer` on
//! other Apple platforms. Note that `AVSpeechSynthesizer` _is_ available on
//! macOS, but only since 10.15!
//!
//! Works on macOS >= 10.7 and iOS > 7.0.
#![deny(unsafe_op_in_unsafe_fn)]

use std::thread;
use std::time::Duration;

use objc2::mutability::InteriorMutable;
use objc2::rc::Id;
use objc2::{extern_class, msg_send, msg_send_id, ClassType};
use objc2_foundation::{ns_string, NSObject, NSString};

#[cfg(target_os = "macos")]
mod appkit {
    use objc2_foundation::NSCopying;
    use std::cell::Cell;

    use super::*;

    #[link(name = "AppKit", kind = "framework")]
    extern "C" {}

    extern_class!(
        /// <https://developer.apple.com/documentation/appkit/nsspeechsynthesizer?language=objc>
        pub(crate) struct Synthesizer;

        unsafe impl ClassType for Synthesizer {
            type Super = NSObject;
            type Mutability = InteriorMutable;
            const NAME: &'static str = "NSSpeechSynthesizer";
        }
    );

    impl Synthesizer {
        // Uses default voice
        pub(crate) fn new() -> Id<Self> {
            unsafe { msg_send_id![Self::class(), new] }
        }

        fn set_rate(&self, rate: f32) {
            unsafe { msg_send![self, setRate: rate] }
        }

        fn set_volume(&self, volume: f32) {
            unsafe { msg_send![self, setVolume: volume] }
        }

        fn start_speaking(&self, s: &NSString) {
            let _: bool = unsafe { msg_send![self, startSpeakingString: s] };
        }

        pub(crate) fn speak(&self, utterance: &Utterance) {
            // Convert to the range 90-720 that `NSSpeechSynthesizer` seems to
            // support
            //
            // Note that you'd probably want a nonlinear conversion here to
            // make it match `AVSpeechSynthesizer`.
            self.set_rate(90.0 + (utterance.rate.get() * (360.0 - 90.0)));
            self.set_volume(utterance.volume.get());
            self.start_speaking(&utterance.string);
        }

        pub(crate) fn is_speaking(&self) -> bool {
            unsafe { msg_send![self, isSpeaking] }
        }
    }

    // Shim to make NSSpeechSynthesizer work similar to AVSpeechSynthesizer
    pub(crate) struct Utterance {
        rate: Cell<f32>,
        volume: Cell<f32>,
        string: Id<NSString>,
    }

    impl Utterance {
        pub(crate) fn new(string: &NSString) -> Self {
            Self {
                rate: Cell::new(0.5),
                volume: Cell::new(1.0),
                string: string.copy(),
            }
        }

        pub(crate) fn set_rate(&self, rate: f32) {
            self.rate.set(rate);
        }

        pub(crate) fn set_volume(&self, volume: f32) {
            self.volume.set(volume);
        }
    }
}

#[cfg(not(target_os = "macos"))]
mod avfaudio {
    use super::*;

    #[link(name = "AVFoundation", kind = "framework")]
    extern "C" {}

    extern_class!(
        /// <https://developer.apple.com/documentation/avfaudio/avspeechsynthesizer?language=objc>
        #[derive(Debug)]
        pub(crate) struct Synthesizer;

        unsafe impl ClassType for Synthesizer {
            type Super = NSObject;
            type Mutability = InteriorMutable;
            const NAME: &'static str = "AVSpeechSynthesizer";
        }
    );

    impl Synthesizer {
        pub(crate) fn new() -> Id<Self> {
            unsafe { msg_send_id![Self::class(), new] }
        }

        pub(crate) fn speak(&self, utterance: &Utterance) {
            unsafe { msg_send![self, speakUtterance: utterance] }
        }

        pub(crate) fn is_speaking(&self) -> bool {
            unsafe { msg_send![self, isSpeaking] }
        }
    }

    extern_class!(
        /// <https://developer.apple.com/documentation/avfaudio/avspeechutterance?language=objc>
        #[derive(Debug)]
        pub struct Utterance;

        unsafe impl ClassType for Utterance {
            type Super = NSObject;
            type Mutability = InteriorMutable;
            const NAME: &'static str = "AVSpeechUtterance";
        }
    );

    impl Utterance {
        pub(crate) fn new(string: &NSString) -> Id<Self> {
            unsafe { msg_send_id![Self::alloc(), initWithString: string] }
        }

        pub(crate) fn set_rate(&self, rate: f32) {
            unsafe { msg_send![self, setRate: rate] }
        }

        pub(crate) fn set_volume(&self, volume: f32) {
            unsafe { msg_send![self, setVolume: volume] }
        }
    }
}

#[cfg(target_os = "macos")]
use appkit::{Synthesizer, Utterance};
#[cfg(not(target_os = "macos"))]
use avfaudio::{Synthesizer, Utterance};

fn main() {
    let synthesizer = Synthesizer::new();
    let utterance = Utterance::new(ns_string!("Hello from Rust!"));
    utterance.set_rate(0.5);
    utterance.set_volume(0.5);
    synthesizer.speak(&utterance);

    // Wait until speech has properly started up
    thread::sleep(Duration::from_millis(1000));
    // Wait until finished speaking
    while synthesizer.is_speaking() {
        thread::sleep(Duration::from_millis(100));
    }
}

Modules§

  • arrayNSArray
    Utilities for the NSArray and NSMutableArray classes.
  • dictionaryNSDictionary
    Utilities for the NSDictionary and NSMutableDictionary classes.
  • enumeratorNSEnumerator
    Utilities for the NSEnumerator class.
  • setNSSet
    Utilities for the NSSet and NSMutableSet classes.

Macros§

Structs§

Enums§

Constants§

Statics§

Traits§

Functions§

Type Aliases§