objc2_ui_kit/
application.rs

1#![cfg(feature = "UIResponder")]
2use core::ffi::{c_char, c_int};
3use core::ptr::NonNull;
4
5use objc2::MainThreadMarker;
6use objc2_foundation::NSString;
7
8use crate::UIApplication;
9
10// These functions are in crt_externs.h.
11extern "C" {
12    fn _NSGetArgc() -> *mut c_int;
13    fn _NSGetArgv() -> *mut *mut *mut c_char;
14}
15
16impl UIApplication {
17    #[allow(clippy::needless_doctest_main)] // Useful to show a full example
18    /// The entry point to UIKit applications.
19    ///
20    /// Creates the application object and the application delegate and sets
21    /// up the event cycle.
22    ///
23    /// See [Apple's documentation][apple-doc] for more details.
24    ///
25    /// [apple-doc]: https://developer.apple.com/documentation/uikit/uiapplicationmain(_:_:_:_:)-1yub7
26    ///
27    /// # Example
28    ///
29    /// Create an application delegate and launch the application.
30    ///
31    /// ```no_run
32    /// use objc2::MainThreadMarker;
33    /// use objc2::rc::{Allocated, Retained};
34    /// use objc2::{define_class, msg_send, ClassType, DefinedClass, MainThreadOnly};
35    /// use objc2_foundation::{NSNotification, NSObject, NSObjectProtocol, NSString};
36    /// use objc2_ui_kit::{UIApplication, UIApplicationDelegate};
37    ///
38    /// #[derive(Default)]
39    /// struct AppState {
40    ///     // Whatever state you want to store in your delegate.
41    /// }
42    ///
43    /// define_class!(
44    ///     // SAFETY:
45    ///     // - `NSObject` does not have any subclassing requirements.
46    ///     // - `AppDelegate` does not implement `Drop`.
47    ///     #[unsafe(super(NSObject))]
48    ///     #[thread_kind = MainThreadOnly]
49    ///     #[ivars = AppState]
50    ///     struct AppDelegate;
51    ///
52    ///     impl AppDelegate {
53    ///         // Called by `UIApplication::main`.
54    ///         #[unsafe(method_id(init))]
55    ///         fn init(this: Allocated<Self>) -> Retained<Self> {
56    ///             let this = this.set_ivars(AppState::default());
57    ///             unsafe { msg_send![super(this), init] }
58    ///         }
59    ///     }
60    ///
61    ///     unsafe impl NSObjectProtocol for AppDelegate {}
62    ///
63    ///     unsafe impl UIApplicationDelegate for AppDelegate {
64    ///         #[unsafe(method(applicationDidFinishLaunching:))]
65    ///         fn did_finish_launching(&self, _notification: &NSNotification) {
66    ///             println!("did finish launching!");
67    ///
68    ///             // Do UI initialization in here, such as creating windows, views, etc.
69    ///         }
70    ///
71    ///         #[unsafe(method(applicationWillTerminate:))]
72    ///         fn will_terminate(&self, _notification: &NSNotification) {
73    ///             println!("will terminate!");
74    ///
75    ///             // Tear down your application state here.
76    ///         }
77    ///     }
78    /// );
79    ///
80    /// fn main() {
81    ///     let mtm = MainThreadMarker::new().unwrap();
82    ///     let delegate_class = NSString::from_class(AppDelegate::class());
83    ///     UIApplication::main(None, Some(&delegate_class), mtm);
84    /// }
85    /// ```
86    #[doc(alias = "UIApplicationMain")]
87    pub fn main(
88        principal_class_name: Option<&NSString>,
89        delegate_class_name: Option<&NSString>,
90        mtm: MainThreadMarker,
91    ) -> ! {
92        // UIApplicationMain must be called on the main thread.
93        let _ = mtm;
94
95        // NOTE: `UIApplicationMain` ignores `argc` and `argv`, so we choose
96        // to not expose those in our API.
97        // We pass correct values anyhow though, just to be certain.
98        let argc = unsafe { *_NSGetArgc() };
99        let argv = unsafe { NonNull::new(*_NSGetArgv()).unwrap().cast() };
100
101        // SAFETY: `argc` and `argv` are correct.
102        // `UIApplicationMain` is safely re-entrant, just weird to do so.
103        let _ret = unsafe { Self::__main(argc, argv, principal_class_name, delegate_class_name) };
104
105        // UIApplicationMain is documented to never return, so whatever we do
106        // here is just for show really.
107        #[cfg(feature = "std")]
108        {
109            std::process::exit(_ret as i32)
110        }
111        #[cfg(not(feature = "std"))]
112        {
113            unreachable!("UIApplicationMain should not have returned")
114        }
115    }
116}