Skip to content

Commit 2a8d69e

Browse files
committed
fix init and window creation flow for iOS and disabled multiscene
1 parent f5bddfe commit 2a8d69e

File tree

4 files changed

+61
-49
lines changed

4 files changed

+61
-49
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ scopeguard = "1.2"
155155
[target."cfg(target_os = \"ios\")".dependencies]
156156
objc2-ui-kit = { version = "0.3", features = ["UIResponder"] }
157157
objc2-foundation = { version = "0.3", default-features = false }
158-
dispatch = "0.2"
159158

160159
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
161160
gtk = "0.18"

src/platform_impl/ios/app_state.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::{
2828
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CGRect, CGSize, NSInteger,
2929
NSOperatingSystemVersion, NSUInteger,
3030
},
31-
scene::app_supports_multiple_scenes,
31+
scene::multiple_scenes_enabled,
3232
},
3333
window::WindowId as RootWindowId,
3434
};
@@ -504,17 +504,15 @@ pub unsafe fn scene_by_id(id: &str) -> Option<Retained<UIScene>> {
504504
}
505505

506506
pub unsafe fn connect_scene(scene: &UIScene) {
507+
eprintln!("connect scene");
507508
let did_first_scene_connect = AppState::get_mut().did_first_scene_connect;
508509
// on scene mode, we run on_app_ready() when the main scene is connected
509510
// instead of on AppDelegate::didFinishLaunching
510511
// this optimizes app startup, since the first created window can immediately see the main scene
511512
// instead of having to create a new one (since it can't synchronously wait for it to be connected)
512513
if !did_first_scene_connect {
513514
AppState::get_mut().did_first_scene_connect = true;
514-
// run it a bit later so the scene is actually marked as connected
515-
// otherwise UIWindowScene::windows returns 0, never connects the window
516-
// and the main scene gets in a weird state where it can't snapshot when we create additional windows
517-
dispatch::Queue::main().exec_async(|| unsafe { on_app_ready() });
515+
on_app_ready();
518516
}
519517

520518
if let Some(window_scene) = scene.downcast_ref::<UIWindowScene>() {
@@ -607,8 +605,8 @@ pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
607605

608606
// requires main thread
609607
pub unsafe fn did_finish_launching() {
610-
// when app supports multiple scenes, we defer the did_finish_launching call to the first scene setup
611-
if !app_supports_multiple_scenes() {
608+
// when app is run in scenes lifecycle mode, we defer the did_finish_launching call to the first scene setup
609+
if !multiple_scenes_enabled() {
612610
on_app_ready();
613611
}
614612
}

src/platform_impl/ios/scene.rs

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
11
// Copyright 2021-2025 Tauri Programme within The Commons Conservancy
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use objc2::{define_class, rc::Retained, MainThreadOnly};
4+
use objc2::{define_class, rc::Retained, MainThreadMarker, MainThreadOnly};
55
use objc2_foundation::{
66
NSBundle, NSDictionary, NSError, NSNumber, NSObject, NSObjectProtocol, NSSet, NSString,
77
NSUserActivity,
88
};
99
use objc2_ui_kit::{
10-
UIOpenURLContext, UIScene, UISceneConnectionOptions, UISceneDelegate, UISceneSession,
11-
UIWindowScene,
10+
UIApplication, UIOpenURLContext, UIScene, UISceneConnectionOptions, UISceneDelegate,
11+
UISceneSession, UIWindowScene,
1212
};
1313

1414
use crate::{
1515
event::{Event, WindowEvent},
16-
platform_impl::platform::{app_state, event_loop::EventWrapper, ffi::id},
16+
platform_impl::platform::{app_state, event_loop::EventWrapper},
1717
window::WindowId as RootWindowId,
1818
};
1919

20+
// true when the system allows the app to display multiple scenes and multiple_scenes_enabled() returns true
21+
// https://developer.apple.com/documentation/uikit/uiapplication/supportsmultiplescenes?language=objc
2022
pub unsafe fn app_supports_multiple_scenes() -> bool {
21-
let application: id = msg_send![class!(UIApplication), sharedApplication];
22-
// this function can be called before the UIApplication is set up (class delegate registration)
23-
if application == std::ptr::null_mut() {
24-
let bundle = NSBundle::mainBundle();
25-
let Some(info) = bundle.infoDictionary() else {
26-
return false;
27-
};
28-
29-
let key = NSString::from_str("UIApplicationSceneManifest");
30-
let Some(manifest) = (*info).objectForKey(&key) else {
31-
return false;
32-
};
33-
34-
let manifest_dict = Retained::cast_unchecked::<NSDictionary<NSString, NSObject>>(manifest);
35-
let supports_key = NSString::from_str("UIApplicationSupportsMultipleScenes");
36-
let Some(value) = (*manifest_dict).objectForKey(&supports_key) else {
37-
return false;
38-
};
39-
40-
let num = Retained::cast_unchecked::<NSNumber>(value);
41-
(*num).as_bool()
42-
} else {
43-
msg_send![application, supportsMultipleScenes]
44-
}
23+
let mtm = MainThreadMarker::new().unwrap();
24+
let application = UIApplication::sharedApplication(mtm);
25+
application.supportsMultipleScenes()
26+
}
27+
28+
// check whether the app's Info.plist enabled multiple scenes
29+
pub unsafe fn multiple_scenes_enabled() -> bool {
30+
let bundle = NSBundle::mainBundle();
31+
let Some(info) = bundle.infoDictionary() else {
32+
return false;
33+
};
34+
35+
let key = NSString::from_str("UIApplicationSceneManifest");
36+
let Some(manifest) = (*info).objectForKey(&key) else {
37+
return false;
38+
};
39+
40+
let manifest_dict = Retained::cast_unchecked::<NSDictionary<NSString, NSObject>>(manifest);
41+
let supports_key = NSString::from_str("UIApplicationSupportsMultipleScenes");
42+
let Some(value) = (*manifest_dict).objectForKey(&supports_key) else {
43+
return false;
44+
};
45+
46+
let num = Retained::cast_unchecked::<NSNumber>(value);
47+
(*num).as_bool()
4548
}
4649

4750
define_class!(
@@ -131,7 +134,6 @@ define_class!(
131134
&self,
132135
_scene: &UIScene,
133136
) -> Option<std::ptr::NonNull<NSUserActivity>> {
134-
log::info!("scene erstore");
135137
None
136138
}
137139

@@ -141,7 +143,6 @@ define_class!(
141143
_scene: &UIScene,
142144
_state_restoration_activity: &NSUserActivity,
143145
) {
144-
log::info!("scene erstorasdasdsade");
145146
}
146147

147148
#[unsafe(method(scene:willContinueUserActivityWithType:))]
@@ -150,13 +151,10 @@ define_class!(
150151
_scene: &UIScene,
151152
_user_activity_type: &NSString,
152153
) {
153-
log::info!("scene erstore ytype");
154154
}
155155

156156
#[unsafe(method(scene:continueUserActivity:))]
157-
fn scene_continueUserActivity(&self, _scene: &UIScene, _user_activity: &NSUserActivity) {
158-
log::info!("scene continue");
159-
}
157+
fn scene_continueUserActivity(&self, _scene: &UIScene, _user_activity: &NSUserActivity) {}
160158

161159
#[unsafe(method(scene:didFailToContinueUserActivityWithType:error:))]
162160
fn scene_didFailToContinueUserActivityWithType_error(
@@ -165,12 +163,9 @@ define_class!(
165163
_user_activity_type: &NSString,
166164
_error: &NSError,
167165
) {
168-
log::info!("scene fail");
169166
}
170167

171168
#[unsafe(method(scene:didUpdateUserActivity:))]
172-
fn scene_didUpdateUserActivity(&self, _scene: &UIScene, _user_activity: &NSUserActivity) {
173-
log::info!("scene updated");
174-
}
169+
fn scene_didUpdateUserActivity(&self, _scene: &UIScene, _user_activity: &NSUserActivity) {}
175170
}
176171
);

src/platform_impl/ios/view.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::{
2626
id, nil, CGFloat, CGPoint, CGRect, UIForceTouchCapability, UIInterfaceOrientationMask,
2727
UIRectEdge, UITouchPhase, UITouchType, BOOL, NO, YES,
2828
},
29-
scene::app_supports_multiple_scenes,
29+
scene::{app_supports_multiple_scenes, multiple_scenes_enabled},
3030
window::PlatformSpecificWindowBuilderAttributes,
3131
DeviceId,
3232
},
@@ -534,10 +534,28 @@ pub unsafe fn create_window(
534534
"Failed to initialize `UIWindow` instance"
535535
);
536536

537-
if app_supports_multiple_scenes() {
537+
if multiple_scenes_enabled() {
538+
// if multiple scenes is enabled in Info.plist, we need to assign it
539+
// regarless of whether the device supports multiple scenes or not
538540
if let Some(scene) = app_state::unitialized_scene() {
539541
let _: () = msg_send![window, setWindowScene: Retained::as_ptr(&scene)];
542+
} else if !app_supports_multiple_scenes() {
543+
// if there's no unitialized scene and the app does not support multiple scenes
544+
// we need to move this window to the main scene, otherwise it won't be visible
545+
let scene = unsafe {
546+
let mtm = MainThreadMarker::new().unwrap();
547+
let application = UIApplication::sharedApplication(mtm);
548+
application.connectedScenes().iter().next()
549+
};
550+
let scene = scene
551+
.as_ref()
552+
.map(|s| Retained::as_ptr(s))
553+
.unwrap_or(std::ptr::null_mut());
554+
555+
let _: () = msg_send![window, setWindowScene: scene];
540556
} else {
557+
// only request a new scene if the system actually supports it
558+
// otherwise it is silently ignored
541559
unsafe {
542560
let mtm = MainThreadMarker::new().unwrap();
543561
let application = UIApplication::sharedApplication(mtm);
@@ -552,6 +570,8 @@ pub unsafe fn create_window(
552570
options.setRequestingScene(Some(&scene));
553571
}
554572

573+
// alternative function is iOS 17+
574+
#[allow(deprecated)]
555575
application.requestSceneSessionActivation_userActivity_options_errorHandler(
556576
None,
557577
None,
@@ -716,7 +736,7 @@ pub fn create_delegate_class() {
716736
did_finish_launching as extern "C" fn(_, _, _, _) -> _,
717737
);
718738

719-
if app_supports_multiple_scenes() {
739+
if multiple_scenes_enabled() {
720740
decl.add_method(
721741
sel!(application:configurationForConnectingSceneSession:options:),
722742
configuration_for_connecting_scene_session as extern "C" fn(_, _, _, _, _) -> _,

0 commit comments

Comments
 (0)