macro_rules! extern_protocol {
(
// The special #[name = $name:literal] attribute is supported here.
$(#[$($attrs:tt)*])*
$v:vis unsafe trait $protocol:ident $(: $conforms_to:ident $(+ $conforms_to_rest:ident)*)? {
$($methods:tt)*
}
) => { ... };
}Expand description
Create a new trait to represent a protocol.
This is similar to a @protocol declaration in Objective-C.
See Protocols - The Objective-C Programming Language and Working with Protocols - Programming with Objective-C for general information about protocols in Objective-C.
This macro will create an unsafe trait with methods that provide access
to the functionality exposed by the protocol.
Conforming to the protocol can be done in two ways:
- For external classes, use the
extern_conformance!macro. - For custom classes created with the
define_class!macro, implement the trait inside the macro.
Objective-C has a smart feature where you can write id<MyProtocol>, and
then work with the protocol as-if it was an object; this is very similar
to dyn traits in Rust, and we implement it in a similar way, see
ProtocolObject for details.
§Specification
The syntax is similar enough to Rust syntax that if you invoke the macro
with parentheses (as opposed to curly brackets), rustfmt will be able to
format the contents.
This macro creates an unsafe trait with the specified methods. A default
implementation of the method is generated based on the selector specified
with #[unsafe(method(a:selector:))]. Similar to extern_methods!, you
can use the #[unsafe(method_family = ...)] attribute to override the
inferred method family.
Other protocols that this protocol conforms to / inherits can be specified as supertraits.
The new trait T is automatically implemented for
ProtocolObject<dyn T>, which also means that ProtocolType is
implemented for dyn T.
Finally, you can use the #[optional] attribute to mark optional methods.
This currently doesn’t have any effect, but probably will have one in the
future when implementing protocols in define_class!.
This macro otherwise shares similarities with extern_class! and
extern_methods!.
§Safety
The following are required for using the macro itself:
- The specified name must be an existing Objective-C protocol.
- The protocol must actually inherit/conform to the protocols specified as supertraits.
Each method is annotated with #[unsafe(method(...))], where you are
responsible for ensuring that the declaration is correct.
While the following are required when implementing the unsafe trait for
a new type:
- The type must represent an object that implements the protocol.
§Examples
Create a trait to represent the NSItemProviderWriting protocol (in
practice, you would import this from objc2-foundation, this is just for
demonstration purposes).
use std::ffi::c_void;
use objc2::ffi::NSInteger;
use objc2::rc::Retained;
use objc2::runtime::{NSObject, NSObjectProtocol};
use objc2::extern_protocol;
use objc2_foundation::{NSArray, NSString, NSProgress, NSItemProviderRepresentationVisibility};
extern_protocol!(
/// This comment will appear on the trait as expected.
//
// We could have named the trait something else on the Rust-side, and
// then used this to keep it correct from Objective-C.
// #[name = "NSItemProviderWriting"]
//
// SAFETY:
// - The name is correct.
// - The protocol does inherit from `NSObjectProtocol`.
pub unsafe trait NSItemProviderWriting: NSObjectProtocol {
// ^^^^^^^^^^^^^^^^
// This protocol inherits from the `NSObject` protocol
// This method we mark as `unsafe`, since we aren't using the
// correct type for the completion handler.
#[unsafe(method(loadDataWithTypeIdentifier:forItemProviderCompletionHandler:))]
unsafe fn loadData(
&self,
type_identifier: &NSString,
completion_handler: *mut c_void,
) -> Option<Retained<NSProgress>>;
// SAFETY: The method is correctly specified.
#[unsafe(method(writableTypeIdentifiersForItemProvider))]
fn writableTypeIdentifiersForItemProvider_class()
-> Retained<NSArray<NSString>>;
// The rest of these are optional, which means that a user of
// `define_class!` would not need to implement them.
// SAFETY: The method is correctly specified.
#[unsafe(method(writableTypeIdentifiersForItemProvider))]
#[optional]
fn writableTypeIdentifiersForItemProvider(&self)
-> Retained<NSArray<NSString>>;
// SAFETY: The method is correctly specified.
#[unsafe(method(itemProviderVisibilityForRepresentationWithTypeIdentifier:))]
#[optional]
fn itemProviderVisibilityForRepresentation_class(
type_identifier: &NSString,
) -> NSItemProviderRepresentationVisibility;
// SAFETY: The method is correctly specified.
#[unsafe(method(itemProviderVisibilityForRepresentationWithTypeIdentifier:))]
#[optional]
fn itemProviderVisibilityForRepresentation(
&self,
type_identifier: &NSString,
) -> NSItemProviderRepresentationVisibility;
}
);
// Types can now implement `NSItemProviderWriting`, and use the methods
// from it as we specified.See the source code of objc2-foundation for many more examples.