2021-03-27 07:29:39 +11:00
|
|
|
//! Implements some functionality to handle dynamically setting the `NSBundle` identifier.
|
2020-02-28 13:34:34 +11:00
|
|
|
//!
|
2021-03-27 07:29:39 +11:00
|
|
|
//!
|
|
|
|
//
|
|
|
|
// This is not currently in use, but does have places where it's useful... and to be honest I'm
|
|
|
|
// kinda happy this is done as a swizzling implementation in pure Rust, which I couldn't find
|
|
|
|
// examples of anywhere else.
|
|
|
|
//
|
|
|
|
// Disregard until you can't, I guess.
|
2020-02-28 13:34:34 +11:00
|
|
|
|
|
|
|
use std::ffi::CString;
|
|
|
|
use std::mem;
|
|
|
|
|
|
|
|
use objc::{class, msg_send, sel, sel_impl, Encode, Encoding, EncodeArguments, Message};
|
|
|
|
use objc::runtime::{Class, Sel, Method, Object, Imp};
|
|
|
|
use objc::runtime::{
|
|
|
|
objc_getClass,
|
|
|
|
class_addMethod,
|
|
|
|
class_getInstanceMethod,
|
|
|
|
method_exchangeImplementations
|
|
|
|
};
|
|
|
|
|
2020-03-18 10:55:09 +11:00
|
|
|
use crate::foundation::{id, nil, BOOL, YES, NSString};
|
|
|
|
|
2020-02-28 13:34:34 +11:00
|
|
|
/// Types that can be used as the implementation of an Objective-C method.
|
|
|
|
pub trait MethodImplementation {
|
|
|
|
/// The callee type of the method.
|
|
|
|
type Callee: Message;
|
|
|
|
/// The return type of the method.
|
|
|
|
type Ret: Encode;
|
|
|
|
/// The argument types of the method.
|
|
|
|
type Args: EncodeArguments;
|
|
|
|
|
|
|
|
/// Returns self as an `Imp` of a method.
|
|
|
|
fn imp(self) -> Imp;
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! method_decl_impl {
|
|
|
|
(-$s:ident, $r:ident, $f:ty, $($t:ident),*) => (
|
|
|
|
impl<$s, $r $(, $t)*> MethodImplementation for $f
|
|
|
|
where $s: Message, $r: Encode $(, $t: Encode)* {
|
|
|
|
type Callee = $s;
|
|
|
|
type Ret = $r;
|
|
|
|
type Args = ($($t,)*);
|
|
|
|
|
|
|
|
fn imp(self) -> Imp {
|
|
|
|
unsafe { mem::transmute(self) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
($($t:ident),*) => (
|
|
|
|
method_decl_impl!(-T, R, extern fn(&T, Sel $(, $t)*) -> R, $($t),*);
|
|
|
|
method_decl_impl!(-T, R, extern fn(&mut T, Sel $(, $t)*) -> R, $($t),*);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
method_decl_impl!();
|
|
|
|
method_decl_impl!(A);
|
|
|
|
|
|
|
|
extern fn get_bundle_id(this: &Object, s: Sel, v: id) -> id {
|
|
|
|
unsafe {
|
|
|
|
let bundle = class!(NSBundle);
|
|
|
|
let main_bundle: id = msg_send![bundle, mainBundle];
|
|
|
|
let e: BOOL = msg_send![this, isEqual:main_bundle];
|
|
|
|
if e == YES {
|
|
|
|
let url: id = msg_send![main_bundle, bundleURL];
|
|
|
|
let x: id = msg_send![url, absoluteString];
|
|
|
|
println!("Got here? {:?}", x);
|
|
|
|
unsafe {
|
|
|
|
NSString::alloc(nil).init_str("com.secretkeys.subatomic")
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
msg_send![this, __bundleIdentifier]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe fn swizzle_bundle_id<F>(bundle_id: &str, func: F) where F: MethodImplementation<Callee=Object> {
|
|
|
|
let name = CString::new("NSBundle").unwrap();
|
|
|
|
let cls = objc_getClass(name.as_ptr());
|
|
|
|
|
|
|
|
// let mut cls = class!(NSBundle) as *mut Class;
|
|
|
|
// Class::get("NSBundle").unwrap();
|
|
|
|
// let types = format!("{}{}{}", Encoding::String, <*mut Object>::ENCODING, Sel::ENCODING);
|
|
|
|
|
|
|
|
let added = class_addMethod(
|
|
|
|
cls as *mut Class,
|
|
|
|
sel!(__bundleIdentifier),
|
|
|
|
func.imp(),
|
|
|
|
CString::new("*@:").unwrap().as_ptr()
|
|
|
|
);
|
|
|
|
|
|
|
|
let method1 = class_getInstanceMethod(cls, sel!(bundleIdentifier)) as *mut Method;
|
|
|
|
let method2 = class_getInstanceMethod(cls, sel!(__bundleIdentifier)) as *mut Method;
|
|
|
|
method_exchangeImplementations(method1, method2);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_bundle_id(bundle_id: &str) {
|
|
|
|
unsafe {
|
|
|
|
swizzle_bundle_id(bundle_id, get_bundle_id as extern fn(&Object, _, _) -> id);
|
|
|
|
}
|
|
|
|
}
|