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}