@zeiteisen code is still working on iOS18, but crashes on NSString(utf8String: response) when response is nil, for examples in Shortcuts scripts.
Update:
extension UIInputViewController {
func getHostBundleId() -> String? {
if let id = hostBundleIdValueBefore16 { id } else { hostBundleIdValueFor16 }
}
private var hostBundleIdValueBefore16: String? {
let value = parent?.value(forKey: "_hostBundleID") as? String
return value != "<null>" ? value : nil
}
private var hostBundleIdValueFor16: String? {
guard let pid = parent?.value(forKey: "_hostPID") else { return nil }
let selector = NSSelectorFromString("defaultService")
guard let anyClass: AnyObject = NSClassFromString("PKService"),
let pkService = anyClass as? NSObjectProtocol,
pkService.responds(to: selector),
let serverInis = pkService.perform(selector).takeUnretainedValue() as? NSObjectProtocol
else {
return nil
}
let lities = serverInis.perform(NSSelectorFromString("personalities")).takeUnretainedValue()
let bundleId = Bundle.main.bundleIdentifier ?? ""
guard let infos = lities.object(forKey: bundleId) as? AnyObject,
let info = infos.object(forKey: pid) as? AnyObject,
let con = info.perform(NSSelectorFromString("connection")).takeUnretainedValue() as? NSObjectProtocol
else {
return nil
}
let xpcCon = con.perform(NSSelectorFromString("_xpcConnection")).takeUnretainedValue()
let handle = dlopen("/usr/lib/libc.dylib", RTLD_NOW)
let sym = dlsym(handle, "xpc_connection_copy_bundle_id")
typealias XpcFunc = @convention(c) (AnyObject) -> UnsafePointer<CChar>?
let cFunc = unsafeBitCast(sym, to: XpcFunc.self)
if let response = cFunc(xpcCon) {
let hostBundleId = NSString(utf8String: response)
return hostBundleId as String?
}
return nil
}
}
Here is the updated solution that also supports iOS 16 and 17:
extension KeyboardViewController {
var hostBundleId: String? {
if let id = hostBundleIdValueBefore16 { return id }
return hostBundleIdValueFor16
}
private var hostBundleIdValueBefore16: String? {
let value = parent?.value(forKey: "_hostBundleID") as? String
return value != "<null>" ? value: nil
}
private var hostBundleIdValueFor16: String? {
guard let pid = parent?.value(forKey: "_hostPID") else { return nil }
let selector = NSSelectorFromString("defaultService")
guard let anyClass: AnyObject = NSClassFromString("PKService"),
let pkService = anyClass as? NSObjectProtocol,
pkService.responds(to: selector),
let serverInis = pkService.perform(selector).takeUnretainedValue() as? NSObjectProtocol
else { return nil }
let lities = serverInis.perform(NSSelectorFromString("personalities")).takeUnretainedValue()
let bundleId = Bundle.main.bundleIdentifier ?? ""
guard let infos = lities.object(forKey: bundleId) as? AnyObject,
let info = infos.object(forKey: pid) as? AnyObject,
let con = info.perform(NSSelectorFromString("connection")).takeUnretainedValue() as? NSObjectProtocol
else { return nil }
let xpcCon = con.perform(NSSelectorFromString("_xpcConnection")).takeUnretainedValue()
let handle = dlopen("/usr/lib/libc.dylib", RTLD_NOW)
let sym = dlsym(handle, "xpc_connection_copy_bundle_id")
typealias xpcFunc = @convention(c) (AnyObject) -> UnsafePointer<CChar>
let cFunc = unsafeBitCast(sym, to: xpcFunc.self)
let response = cFunc(xpcCon)
let hostBundleId = NSString(utf8String: response)
return hostBundleId as String?
}
}
Note that you may get rejected by Apple for using this code. However, some apps managed to pass the Apple Review.
Credits: KeyboardKit
Usage:
print(hostBundleId)
// Output in Apple Notes: com.apple.mobilenotes
Make sure you are inside your KeyboardController class.