swift_rs/
swift.rs

1use std::ffi::c_void;
2
3use crate::*;
4
5/// Reference to an `NSObject` for internal use by [`swift!`].
6#[must_use = "A Ref MUST be sent over to the Swift side"]
7#[repr(transparent)]
8pub struct SwiftRef<'a, T: SwiftObject>(&'a SRObjectImpl<T::Shape>);
9
10impl<'a, T: SwiftObject> SwiftRef<'a, T> {
11    pub(crate) unsafe fn retain(&self) {
12        retain_object(self.0 as *const _ as *const c_void)
13    }
14}
15
16/// A type that is represented as an `NSObject` in Swift.
17pub trait SwiftObject {
18    type Shape;
19
20    /// Gets a reference to the `SRObject` at the root of a `SwiftObject`
21    fn get_object(&self) -> &SRObject<Self::Shape>;
22
23    /// Creates a [`SwiftRef`] for an object which can be used when calling a Swift function.
24    /// This function should never be called manually,
25    /// instead you should rely on the [`swift!`] macro to call it for you.
26    ///
27    /// # Safety
28    /// This function converts the [`NonNull`](std::ptr::NonNull)
29    /// inside an [`SRObject`] into a reference,
30    /// implicitly assuming that the pointer is still valid.
31    /// The inner pointer is private,
32    /// and the returned [`SwiftRef`] is bound to the lifetime of the original [`SRObject`],
33    /// so if you use `swift-rs` as normal this function should be safe.
34    unsafe fn swift_ref(&self) -> SwiftRef<Self>
35    where
36        Self: Sized,
37    {
38        SwiftRef(self.get_object().0.as_ref())
39    }
40
41    /// Adds a retain to an object.
42    ///
43    /// # Safety
44    /// Just don't call this, let [`swift!`] handle it for you.
45    unsafe fn retain(&self)
46    where
47        Self: Sized,
48    {
49        self.swift_ref().retain()
50    }
51}
52
53swift!(pub(crate) fn retain_object(obj: *const c_void));
54swift!(pub(crate) fn release_object(obj: *const c_void));
55swift!(pub(crate) fn data_from_bytes(data: *const u8, size: Int) -> SRData);
56swift!(pub(crate) fn string_from_bytes(data: *const u8, size: Int) -> SRString);
57
58/// Declares a function defined in a swift library.
59/// As long as this macro is used, retain counts of arguments
60/// and return values will be correct.
61///
62/// Use this macro as if the contents were going directly
63/// into an `extern "C"` block.
64///
65/// ```
66/// use swift_rs::*;
67///
68/// swift!(fn echo(string: &SRString) -> SRString);
69///
70/// let string: SRString = "test".into();
71/// let result = unsafe { echo(&string) };
72///
73/// assert_eq!(result.as_str(), string.as_str())
74/// ```
75///
76/// # Details
77///
78/// Internally this macro creates a wrapping function around an `extern "C"` block
79/// that represents the actual Swift function. This is done in order to restrict the types
80/// that can be used as arguments and return types, and to ensure that retain counts of returned
81/// values are appropriately balanced.
82#[macro_export]
83macro_rules! swift {
84    ($vis:vis fn $name:ident $(<$($lt:lifetime),+>)? ($($arg:ident: $arg_ty:ty),*) $(-> $ret:ty)?) => {
85        $vis unsafe fn $name $(<$($lt),*>)? ($($arg: $arg_ty),*) $(-> $ret)? {
86            extern "C" {
87                fn $name $(<$($lt),*>)? ($($arg: <$arg_ty as $crate::SwiftArg>::ArgType),*) $(-> $ret)?;
88            }
89
90            let res = {
91                $(let $arg = $crate::SwiftArg::as_arg(&$arg);)*
92
93                $name($($arg),*)
94            };
95
96            $crate::SwiftRet::retain(&res);
97
98            res
99        }
100    };
101}