macro_rules! extern_methods {
    (
        $(
            $(#[$impl_m:meta])*
            unsafe impl<$($t:ident $(: $b:ident $(+ $rest:ident)*)?),*> $type:ty {
                $($methods:tt)*
            }
        )+
    ) => { ... };
    (
        $(
            $(#[$impl_m:meta])*
            unsafe impl $type:ty {
                $($methods:tt)*
            }
        )+
    ) => { ... };
}
Expand description

Define methods on an external class.

This is a convenience macro to easily generate associated functions and methods that call msg_send! appropriately.

Specification

Within the impl block you can define two types of functions without bodies; “associated functions” and “methods”. These are then mapped to the Objective-C equivalents “class methods” and “instance methods”, and an appropriate body is created for you. In particular, if you use self your method will assumbed to be an instance method, and if you don’t it will be assumed to be a class method.

The desired selector can be specified using the #[sel(my:selector:)] attribute. The name of the function doesn’t matter.

If you specify a function/method with a body, the macro will simply ignore it.

Safety

You must ensure that any methods you declare with the #[sel(...)] attribute upholds the safety guarantees decribed in the msg_send! macro, or are marked unsafe.

Examples

Let’s create a quick interface to the NSCalendar class:

use objc2::foundation::{NSObject, NSRange, NSString, NSUInteger};
use objc2::rc::{Id, Shared};
use objc2::{extern_class, extern_methods, msg_send_id, Encode, Encoding, ClassType};

extern_class!(
    #[derive(PartialEq, Eq, Hash)]
    pub struct NSCalendar;

    unsafe impl ClassType for NSCalendar {
        type Super = NSObject;
    }
);

pub type NSCalendarIdentifier = NSString;

#[repr(usize)] // NSUInteger
pub enum NSCalendarUnit {
    Hour = 32,
    Minute = 64,
    Second = 128,
    // TODO: More units
}

unsafe impl Encode for NSCalendarUnit {
    const ENCODING: Encoding = usize::ENCODING;
}

extern_methods!(
    /// Creation methods.
    // TODO: Support methods returning `Id`
    unsafe impl NSCalendar {
        pub fn current() -> Id<Self, Shared> {
            unsafe { msg_send_id![Self::class(), currentCalendar] }
        }

        pub fn new(identifier: &NSCalendarIdentifier) -> Id<Self, Shared> {
            unsafe {
                msg_send_id![
                    msg_send_id![Self::class(), alloc],
                    initWithCalendarIdentifier: identifier,
                ]
            }
        }
    }

    /// Accessor methods.
    // SAFETY: `first_weekday` is correctly defined
    unsafe impl NSCalendar {
        #[sel(firstWeekday)]
        pub fn first_weekday(&self) -> NSUInteger;

        pub fn am_symbol(&self) -> Id<NSString, Shared> {
            unsafe { msg_send_id![self, amSymbol] }
        }

        #[sel(date:matchesComponents:)]
        // `unsafe` because we don't have definitions for `NSDate` and
        // `NSDateComponents` yet, so the user must ensure that is what's
        // passed.
        pub unsafe fn date_matches(&self, date: &NSObject, components: &NSObject) -> bool;

        #[sel(maximumRangeOfUnit:)]
        pub fn max_range(&self, unit: NSCalendarUnit) -> NSRange;
    }
);

The extern_methods! declaration then becomes:

/// Creation methods.
impl NSCalendar {
    pub fn current() -> Id<Self, Shared> {
        unsafe { msg_send_id![Self::class(), currentCalendar] }
    }

    pub fn new(identifier: &NSCalendarIdentifier) -> Id<Self, Shared> {
        unsafe {
            msg_send_id![
                msg_send_id![Self::class(), alloc],
                initWithCalendarIdentifier: identifier,
            ]
        }
    }
}

/// Accessor methods.
impl NSCalendar {
    pub fn first_weekday(&self) -> NSUInteger {
        unsafe { msg_send![self, firstWeekday] }
    }

    pub fn am_symbol(&self) -> Id<NSString, Shared> {
        unsafe { msg_send_id![self, amSymbol] }
    }

    pub unsafe fn date_matches(&self, date: &NSObject, components: &NSObject) -> bool {
        unsafe { msg_send![self, date: date, matchesComponents: components] }
    }

    pub fn max_range(&self, unit: NSCalendarUnit) -> NSRange {
        unsafe { msg_send![self, maximumRangeOfUnit: unit] }
    }
}