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}