[ios] Fix iOS demo build.
Several recent changes to the way that subclass registration is done inadvertently impacted part of how the iOS side of things works. iOS app initialization requires passing known class names to `UIApplicationMain` to let the system instantiate things; we append a random suffix to class names by default to avoid issues on macOS where classes that are being loaded from a bundle seem to collide in the ObjC runtime. This change brings in two new methods in `foundation/class` to explicitly bypass the dynamic subclass suffix generation for the rare cases (such as the above) where we know we need it.
This commit is contained in:
parent
44ad4886e7
commit
3e3122f54c
|
@ -58,6 +58,11 @@ impl ClassMap {
|
||||||
ClassMap(RwLock::new(HashMap::new()))
|
ClassMap(RwLock::new(HashMap::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A publicly accessible load method that just passes through our global singleton.
|
||||||
|
pub fn static_load(class_name: &'static str, superclass_name: Option<&'static str>) -> Option<*const Class> {
|
||||||
|
CLASSES.load(class_name, superclass_name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to load a previously registered class.
|
/// Attempts to load a previously registered class.
|
||||||
///
|
///
|
||||||
/// This checks our internal map first, and then calls out to the Objective-C runtime to ensure
|
/// This checks our internal map first, and then calls out to the Objective-C runtime to ensure
|
||||||
|
@ -110,6 +115,31 @@ impl ClassMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calls through to `load_or_register_class_with_optional_generated_suffix`, specifying that we
|
||||||
|
/// should append a random suffix to the generated class name. This is important for situations
|
||||||
|
/// where we may be loading classes from e.g two different bundles and need to avoid collision.
|
||||||
|
///
|
||||||
|
/// Some parts of the codebase (e.g, iOS UIApplication registration) may need to know the name
|
||||||
|
/// ahead of time and are not concerned about potential duplications. These cases should feel free
|
||||||
|
/// to call through to `load_or_register_class_with_optional_generated_suffix` directly, as they
|
||||||
|
/// are comparatively rare in nature.
|
||||||
|
///
|
||||||
|
/// > In the future, this indirection may be removed and the return type of
|
||||||
|
/// > `load_or_register_class_with_optional_generated_suffix` will be altered to return the generated
|
||||||
|
/// > class name - but most cases do not need this and it would be a larger change to orchestrate at
|
||||||
|
/// > the moment.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn load_or_register_class<F>(
|
||||||
|
superclass_name: &'static str,
|
||||||
|
subclass_name: &'static str,
|
||||||
|
config: F
|
||||||
|
) -> *const Class
|
||||||
|
where
|
||||||
|
F: Fn(&mut ClassDecl) + 'static
|
||||||
|
{
|
||||||
|
load_or_register_class_with_optional_generated_suffix(superclass_name, subclass_name, true, config)
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to load a subclass, given a `superclass_name` and subclass_name. If
|
/// Attempts to load a subclass, given a `superclass_name` and subclass_name. If
|
||||||
/// the subclass cannot be loaded, it's dynamically created and injected into
|
/// the subclass cannot be loaded, it's dynamically created and injected into
|
||||||
/// the runtime, and then returned. The returned value can be used for allocating new instances of
|
/// the runtime, and then returned. The returned value can be used for allocating new instances of
|
||||||
|
@ -124,7 +154,12 @@ impl ClassMap {
|
||||||
///
|
///
|
||||||
/// There's definitely room to optimize here, but it works for now.
|
/// There's definitely room to optimize here, but it works for now.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn load_or_register_class<F>(superclass_name: &'static str, subclass_name: &'static str, config: F) -> *const Class
|
pub fn load_or_register_class_with_optional_generated_suffix<F>(
|
||||||
|
superclass_name: &'static str,
|
||||||
|
subclass_name: &'static str,
|
||||||
|
should_append_random_subclass_name_suffix: bool,
|
||||||
|
config: F
|
||||||
|
) -> *const Class
|
||||||
where
|
where
|
||||||
F: Fn(&mut ClassDecl) + 'static
|
F: Fn(&mut ClassDecl) + 'static
|
||||||
{
|
{
|
||||||
|
@ -141,7 +176,8 @@ where
|
||||||
// guarantees that we almost always have a unique name to register with the ObjC runtime).
|
// guarantees that we almost always have a unique name to register with the ObjC runtime).
|
||||||
//
|
//
|
||||||
// For more context, see: https://github.com/ryanmcgrath/cacao/issues/63
|
// For more context, see: https://github.com/ryanmcgrath/cacao/issues/63
|
||||||
let objc_subclass_name = format!(
|
let objc_subclass_name = match should_append_random_subclass_name_suffix {
|
||||||
|
true => format!(
|
||||||
"{}_{}_{}",
|
"{}_{}_{}",
|
||||||
subclass_name,
|
subclass_name,
|
||||||
superclass_name,
|
superclass_name,
|
||||||
|
@ -151,7 +187,10 @@ where
|
||||||
let t = u128::from(s) * (u128::from(s ^ 0xe7037ed1a0b428db));
|
let t = u128::from(s) * (u128::from(s ^ 0xe7037ed1a0b428db));
|
||||||
((t >> 64) as u64) ^ (t as u64)
|
((t >> 64) as u64) ^ (t as u64)
|
||||||
})
|
})
|
||||||
);
|
),
|
||||||
|
|
||||||
|
false => format!("{}_{}", subclass_name, superclass_name)
|
||||||
|
};
|
||||||
|
|
||||||
match ClassDecl::new(&objc_subclass_name, unsafe { &*superclass }) {
|
match ClassDecl::new(&objc_subclass_name, unsafe { &*superclass }) {
|
||||||
Some(mut decl) => {
|
Some(mut decl) => {
|
||||||
|
|
|
@ -28,7 +28,8 @@ mod array;
|
||||||
pub use array::NSArray;
|
pub use array::NSArray;
|
||||||
|
|
||||||
mod class;
|
mod class;
|
||||||
pub use class::load_or_register_class;
|
pub use class::{load_or_register_class, load_or_register_class_with_optional_generated_suffix};
|
||||||
|
pub(crate) use class::ClassMap;
|
||||||
|
|
||||||
mod data;
|
mod data;
|
||||||
pub use data::NSData;
|
pub use data::NSData;
|
||||||
|
|
|
@ -4,9 +4,16 @@
|
||||||
|
|
||||||
use objc::runtime::Class;
|
use objc::runtime::Class;
|
||||||
|
|
||||||
use crate::foundation::load_or_register_class;
|
use crate::foundation::load_or_register_class_with_optional_generated_suffix;
|
||||||
|
|
||||||
/// Used for injecting a custom UIApplication. Currently does nothing.
|
/// Used for injecting a custom UIApplication. Currently does nothing.
|
||||||
pub(crate) fn register_app_class() -> *const Class {
|
pub(crate) fn register_app_class() -> *const Class {
|
||||||
load_or_register_class("UIApplication", "RSTApplication", |decl| unsafe {})
|
let should_generate_suffix = false;
|
||||||
|
|
||||||
|
load_or_register_class_with_optional_generated_suffix(
|
||||||
|
"UIApplication",
|
||||||
|
"RSTApplication",
|
||||||
|
should_generate_suffix,
|
||||||
|
|decl| {}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use objc::runtime::{Class, Object, Sel};
|
||||||
use objc::{sel, sel_impl};
|
use objc::{sel, sel_impl};
|
||||||
|
|
||||||
//use crate::error::Error;
|
//use crate::error::Error;
|
||||||
use crate::foundation::{id, load_or_register_class, BOOL, YES};
|
use crate::foundation::{id, load_or_register_class_with_optional_generated_suffix, BOOL, YES};
|
||||||
use crate::uikit::app::{AppDelegate, APP_DELEGATE};
|
use crate::uikit::app::{AppDelegate, APP_DELEGATE};
|
||||||
use crate::uikit::scene::{SceneConnectionOptions, SceneSession};
|
use crate::uikit::scene::{SceneConnectionOptions, SceneSession};
|
||||||
|
|
||||||
|
@ -41,7 +41,9 @@ extern "C" fn configuration_for_scene_session<T: AppDelegate>(this: &Object, _:
|
||||||
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
||||||
/// pointers we need to have.
|
/// pointers we need to have.
|
||||||
pub(crate) fn register_app_delegate_class<T: AppDelegate>() -> *const Class {
|
pub(crate) fn register_app_delegate_class<T: AppDelegate>() -> *const Class {
|
||||||
load_or_register_class("NSObject", "RSTAppDelegate", |decl| unsafe {
|
let should_generate_suffix = false;
|
||||||
|
|
||||||
|
load_or_register_class_with_optional_generated_suffix("NSObject", "RSTAppDelegate", should_generate_suffix, |decl| unsafe {
|
||||||
// Launching Applications
|
// Launching Applications
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(application:didFinishLaunchingWithOptions:),
|
sel!(application:didFinishLaunchingWithOptions:),
|
||||||
|
|
|
@ -149,13 +149,22 @@ impl<T, W, F> App<T, W, F> {
|
||||||
|
|
||||||
let c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const c_char>>();
|
let c_args = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<*const c_char>>();
|
||||||
|
|
||||||
let mut s = NSString::new("RSTApplication");
|
let mut s = NSString::new("RSTApplication_UIApplication");
|
||||||
let mut s2 = NSString::new("RSTAppDelegate");
|
let mut s2 = NSString::new("RSTAppDelegate_NSObject");
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
println!("RUNNING?!");
|
||||||
UIApplicationMain(c_args.len() as c_int, c_args.as_ptr(), s.into(), s2.into());
|
UIApplicationMain(c_args.len() as c_int, c_args.as_ptr(), s.into(), s2.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pool.drain();
|
//self.pool.drain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T, W, F> Drop for App<T, W, F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("DROPPING");
|
||||||
|
//self.pool.drain();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use objc::runtime::Object;
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
use objc_id::Id;
|
use objc_id::Id;
|
||||||
|
|
||||||
use crate::foundation::{id, NSString};
|
use crate::foundation::{id, NSString, ClassMap};
|
||||||
use crate::uikit::scene::SessionRole;
|
use crate::uikit::scene::SessionRole;
|
||||||
|
|
||||||
/// A wrapper for UISceneConfiguration.
|
/// A wrapper for UISceneConfiguration.
|
||||||
|
@ -15,6 +15,9 @@ impl SceneConfig {
|
||||||
/// Creates a new `UISceneConfiguration` with the specified name and session role, retains it,
|
/// Creates a new `UISceneConfiguration` with the specified name and session role, retains it,
|
||||||
/// and returns it.
|
/// and returns it.
|
||||||
pub fn new(name: &str, role: SessionRole) -> Self {
|
pub fn new(name: &str, role: SessionRole) -> Self {
|
||||||
|
let delegate_class = ClassMap::static_load("RSTWindowSceneDelegate", Some("UIResponder"))
|
||||||
|
.expect("A crucial iOS step was missed - the scene delegate class is either not loaded or misnamed");
|
||||||
|
|
||||||
SceneConfig(unsafe {
|
SceneConfig(unsafe {
|
||||||
let name = NSString::new(name);
|
let name = NSString::new(name);
|
||||||
let role = NSString::from(role);
|
let role = NSString::from(role);
|
||||||
|
@ -23,7 +26,7 @@ impl SceneConfig {
|
||||||
let config: id = msg_send![cls, configurationWithName:name sessionRole:role];
|
let config: id = msg_send![cls, configurationWithName:name sessionRole:role];
|
||||||
|
|
||||||
let _: () = msg_send![config, setSceneClass: class!(UIWindowScene)];
|
let _: () = msg_send![config, setSceneClass: class!(UIWindowScene)];
|
||||||
let _: () = msg_send![config, setDelegateClass: class!(RSTWindowSceneDelegate)];
|
let _: () = msg_send![config, setDelegateClass: delegate_class];
|
||||||
|
|
||||||
Id::from_ptr(config)
|
Id::from_ptr(config)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use objc::runtime::{Class, Object, Protocol, Sel};
|
use objc::runtime::{Class, Object, Protocol, Sel};
|
||||||
use objc::{class, msg_send, sel, sel_impl};
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::foundation::{id, load_or_register_class};
|
use crate::foundation::{id, load_or_register_class_with_optional_generated_suffix};
|
||||||
use crate::uikit::app::SCENE_DELEGATE_VENDOR;
|
use crate::uikit::app::SCENE_DELEGATE_VENDOR;
|
||||||
use crate::uikit::scene::{Scene, SceneConnectionOptions, SceneSession, WindowSceneDelegate};
|
use crate::uikit::scene::{Scene, SceneConnectionOptions, SceneSession, WindowSceneDelegate};
|
||||||
use crate::utils::load;
|
use crate::utils::load;
|
||||||
|
@ -45,7 +45,9 @@ extern "C" fn scene_will_connect_to_session_with_options<T: WindowSceneDelegate>
|
||||||
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
/// Registers an `NSObject` application delegate, and configures it for the various callbacks and
|
||||||
/// pointers we need to have.
|
/// pointers we need to have.
|
||||||
pub(crate) fn register_window_scene_delegate_class<T: WindowSceneDelegate, F: Fn() -> Box<T>>() -> *const Class {
|
pub(crate) fn register_window_scene_delegate_class<T: WindowSceneDelegate, F: Fn() -> Box<T>>() -> *const Class {
|
||||||
load_or_register_class("UIResponder", "RSTWindowSceneDelegate", |decl| unsafe {
|
let should_generate_suffix = false;
|
||||||
|
|
||||||
|
load_or_register_class_with_optional_generated_suffix("UIResponder", "RSTWindowSceneDelegate", false, |decl| unsafe {
|
||||||
let p = Protocol::get("UIWindowSceneDelegate").unwrap();
|
let p = Protocol::get("UIWindowSceneDelegate").unwrap();
|
||||||
|
|
||||||
// A spot to hold a pointer to
|
// A spot to hold a pointer to
|
||||||
|
|
Loading…
Reference in a new issue