1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//! Traits that an implementing application can conform to. These aim to wrap the general
//! lifecycles across macOS/iOS/etc, while still conforming to a Rust-ish approach.
use url::Url;
use crate::error::Error;
use crate::user_activity::UserActivity;
use crate::appkit::app::enums::TerminateResponse;
use crate::appkit::menu::Menu;
use crate::appkit::printing::enums::PrintResponse;
use crate::appkit::printing::settings::PrintSettings;
#[cfg(feature = "cloudkit")]
use crate::cloudkit::share::CKShareMetaData;
/// `AppDelegate` is more or less `NSApplicationDelegate` from the Objective-C/Swift side, just named
/// differently to fit in with the general naming scheme found within this framework. You can
/// implement methods from this trait in order to respond to lifecycle events that the system will
/// fire off.
pub trait AppDelegate {
/// Called right before the application will finish launching. You really, probably, want to do
/// your setup in `did_finish_launching` unless you're sure of what you're doing.
fn will_finish_launching(&self) {}
/// Fired when the application has finished launching. Unlike most other "load" lifecycle
/// events in this framework, you don't get a reference to an app here - if you need to call
/// through to your shared application, then used the `App::shared()` call.
fn did_finish_launching(&self) {}
/// Fired when the application is about to become active.
fn did_become_active(&self) {}
/// Fired when the application is about to resign active state.
fn will_resign_active(&self) {}
/// Fired when the user is going to continue an activity.
fn will_continue_user_activity(&self, _activity_type: &str) -> bool {
false
}
/// Fired when data for continuing an activity is available. Currently, the
/// `restoration_handler` is not used, but there to communicate intent with what this API will
/// eventually be doing.
fn continue_user_activity<F: Fn()>(&self, _activity: UserActivity, _restoration_handler: F) -> bool {
false
}
/// Fired when the activity could not be continued.
fn failed_to_continue_user_activity(&self, _activity_type: &str, _error: Error) {}
/// Fired after the user activity object has been updated.
fn updated_user_activity(&self, _activity: UserActivity) {}
/// Fired when you've successfully registered for remote notifications with APNS.
fn registered_for_remote_notifications(&self, _token: &str) {}
/// Fired after you've received a push notification from APNS.
//fn did_receive_remote_notification(&self, notification: PushNotification) {}
/// Fired if there was a failure to register for remote notifications with APNS - e.g,
/// connection issues or something.
fn failed_to_register_for_remote_notifications(&self, _error: Error) {}
/// Fires after the user accepted a CloudKit sharing invitation associated with your
/// application.
#[cfg(feature = "cloudkit")]
fn user_accepted_cloudkit_share(&self, _share_metadata: CKShareMetaData) {}
/// Fired before the application terminates. You can use this to do any required cleanup.
fn will_terminate(&self) {}
/// Fired immediately before the application is about to become active.
fn will_become_active(&self) {}
/// Fired when the application has resigned active state.
fn did_resign_active(&self) {}
/// Fired when the application is about to hide.
fn will_hide(&self) {}
/// Fired after the application has hidden.
fn did_hide(&self) {}
/// Fired when the application is about to unhide itself.
fn will_unhide(&self) {}
/// Fired after the application has unhidden itself.
fn did_unhide(&self) {}
/// Fired immediately before the application object updates its windows.
fn will_update(&self) {}
/// Fired immediately after the application object updates its windows.
fn did_update(&self) {}
/// This is fired after the `Quit` menu item has been selected, or after you've called `App::terminate()`.
///
/// In most cases you just want `TerminateResponse::Now` here, which enables business as usual. If you need,
/// though, you can cancel the termination via `TerminateResponse::Cancel` to continue something essential. If
/// you do this, you'll need to be sure to call `App::reply_to_termination_request()` to circle
/// back.
fn should_terminate(&self) -> TerminateResponse {
TerminateResponse::Now
}
/// Called after closing the last open window. Return `true` here if you want
/// the application to terminate.
fn should_terminate_after_last_window_closed(&self) -> bool {
false
}
/// Sent by the application to the delegate prior to default behavior to reopen AppleEvents.
///
/// `has_visible_windows` indicates whether the Application object found any visible windows in your application.
/// You can use this value as an indication of whether the application would do anything if you return `true`.
///
/// Return `true` if you want the application to perform its normal tasks, or `false` if you want the
/// application to do nothing. The default implementation of this method returns `true`.
///
/// Some finer points of discussion, from Apple documentation:
///
/// These events are sent whenever the Finder reactivates an already running application because someone
/// double-clicked it again or used the dock to activate it.
///
/// For most document-based applications, an untitled document will be created.
///
/// [Read more
/// here](https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428638-applicationshouldhandlereopen?language=objc)
fn should_handle_reopen(&self, _has_visible_windows: bool) -> bool {
true
}
/// Supply a dock menu for the application dynamically. The default implementation for this
/// method returns `None`, for no menu.
fn dock_menu(&self) -> Option<Menu> {
None
}
/// Fired before the application presents an error message to the user. If you find the error
/// to be... not what you want, you can take it, alter it, and return it anew. The default
/// implementation of this method simply returns the error as-is.
fn will_present_error(&self, error: Error) -> Error {
error
}
/// Fired when the screen parameters for the application have changed (e.g, the user changed
/// something in their settings).
fn did_change_screen_parameters(&self) {}
/// Fired when you have a list of `Url`'s to open. This is best explained by quoting the Apple
/// documentation verbatim:
///
/// _"AppKit calls this method when your app is asked to open one or more URL-based resources.
/// You must declare the URL types that your app supports in your `Info.plist` file using the `CFBundleURLTypes` key.
/// The list can also include URLs for documents for which your app does not have an associated `NSDocument` class.
/// You configure document types by adding the `CFBundleDocumentTypes` key to your Info.plist
/// file."
///
/// Note that since we have this as the de-facto method of handling resource opens, the system
/// will _not_ call `application:openFile:` or `application:openFiles`.
fn open_urls(&self, _urls: Vec<Url>) {}
/// Fired when the file is requested to be opened programmatically. This is not a commonly used
/// or implemented method.
///
/// According to Apple:
///
/// _"The method should open the file without bringing up its application’s user interface—that is,
/// work with the file is under programmatic control of sender, rather than under keyboard control of the user."_
///
/// It's unclear how supported this is in sandbox environments, so use at your own risk.
fn open_file_without_ui(&self, _filename: &str) -> bool {
false
}
/// Fired when the application is ready and able to open a temporary file.
/// Return `true` or `false` here depending on whether the operation was successful.
///
/// It's your responsibility to remove the temp file.
fn open_temp_file(&self, _filename: &str) -> bool {
false
}
/// Fired before attempting to open an untitled file. Return `true` here if you want
/// `open_untitled_file` to be called by the system.
fn should_open_untitled_file(&self) -> bool {
false
}
/// Called when the application has asked you to open a new, untitled file.
/// Returns a `bool` indicating whether the file was successfully opened or not.
fn open_untitled_file(&self) -> bool {
true
}
/// Sent when the user starts up the application on the command line with the -NSPrint option.
/// The application terminates immediately after this method returns. For more information,
/// cosnult the official Apple documentation.
///
/// (You probably never need to implement this, but we support it anyway)
fn print_file(&self, _filename: &str) -> bool {
false
}
/// Called when the user has requested to print some files.
///
/// Returns a `PrintResponse`, indicating status of the print job. You can return
/// `PrintResponse::ReplyLater` if you need to do something like confirming via a popover. If
/// you do this, though, you must call `App::reply_to_open_or_print()` when the operation has
/// been completed.
///
/// Note that macOS has a long-deprecated `printFiles:` method, which your searching may bring
/// up. This method really maps to `application:printFiles:withSettings:showPrintPanels:`, so
/// be sure to just... look there.
fn print_files(&self, _filenames: Vec<String>, _settings: PrintSettings, _show_panels: bool) -> PrintResponse {
PrintResponse::Failure
}
/// Fired when the occlusion state for the app has changed.
///
/// From Apple's docs, as there's no other way to describe this better: _upon receiving this method, you can query the
/// application for its occlusion state. Note that this only notifies about changes in the state of the occlusion, not
/// when the occlusion region changes. You can use this method to increase responsiveness and save power by halting any
/// expensive calculations that the user can not see._
fn occlusion_state_changed(&self) {}
/// Fired when the system wants to know whether your application, via scripting, can handle the
/// key specifying operations.
fn delegate_handles_key(&self, _key: &str) -> bool {
false
}
}