我使用完全相同的方法来混合两个
AppDelegate
实例方法 - 通用和 URL 方案链接。我需要提到的是,两个测试链接都已正确设置。我尝试检查这种方法在 iOS 12 和 13 上的工作原理,并得到了相同的意外行为。
当我尝试单击通用链接时,一切都会按预期进行。在这种情况下,当我尝试对 URL 方案链接执行相同操作时,我发现系统不会启动成功的 swizzled 方法
application(_:open:options:)
。
如果我在
application(_:open:options:)
类中初始化两个空方法 application(_:continue:restorationHandler:)
和 AppDelegate
,然后从下面的代码中调用 method_exchangeImplementations
,我会成功收到 application(_:open:options:)
和 application(_:continue:restorationHandler:)
方法的更改实现。当我尝试通过通用链接或 URL 方案链接进行点击时,在这两种情况下我收到了预期的行为 - 系统触发了我的混合方法。
当我没有在
application(_:open:options:)
类中初始化空方法 application(_:continue:restorationHandler:)
和 AppDelegate
时,您可以从下面的代码中看到,我调用方法 class_addMethod
来动态添加实现到 AppDelegate
实例。在这种情况下,我看到两种方法 application(_:open:options:)
和 application(_:continue:restorationHandler:)
都成功添加到 AppDelegate
实例,但是当我尝试通过 URL 方案链接进行单击时 - 系统尚未触发我的 swizzled 方法 application(_:open:options:)
,反之亦然。与方法 application(_:continue:restorationHandler:)
反之亦然 - 单击通用链接会触发我的 swizzle 方法。
// MARK: Swizzle AppDelegate universal links method.
private func swizzleContinueRestorationHandler() {
guard let swizzleMethod = class_getInstanceMethod(Swizzler.self, #selector(self.application(_:continue:restorationHandler:))) else { return }
let delegateClass: AnyClass! = object_getClass(UIApplication.shared.delegate)
let applicationSelector = #selector(UIApplicationDelegate.application(_:continue:restorationHandler:))
if let originalMethod = class_getInstanceMethod(delegateClass, applicationSelector) {
method_exchangeImplementations(originalMethod, swizzleMethod)
} else {
class_addMethod(delegateClass, applicationSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod))
}
}
// MARK: Swizzle AppDelegate URL schemes method.
private func swizzleOpenOptions() {
guard let swizzleMethod = class_getInstanceMethod(Swizzler.self, #selector(self.application(_:open:options:))) else { return }
let delegateClass: AnyClass! = object_getClass(UIApplication.shared.delegate)
let applicationSelector = #selector(UIApplicationDelegate.application(_:open:options:))
if let originalMethod = class_getInstanceMethod(delegateClass, applicationSelector) {
method_exchangeImplementations(originalMethod, swizzleMethod)
} else {
class_addMethod(delegateClass, applicationSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod))
}
}
// MARK: Swizzled AppDelegate universal links method.
@objc func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
...
}
// MARK: Swizzled AppDelegate URL schemes method.
@objc func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
...
}
为什么会发生这种情况以及如何将方法
application(_:open:options:)
动态添加到 AppDelegate
实例并在单击 URL 方案链接后获得预期的行为?
好吧,4 1/2 年的答案已经很晚了,但至少我确切地知道问题是什么。
当 iOS 将 ApplicationDelegate 加载到
UIApplication.sharedApplication.delegate
时,它会立即检查该对象是否响应某些选择器,并将结果保留在 Application 对象中的某个位置。像这样的东西:
BOOL thisAppHandlesOpenByUrl = [delegate respondsToSelector:@selector(application:openURL:options:)];
...
这些检查是在 UIApplication 的
setDelegate:
中完成的,以后无法更改,因为状态保存在 UIKit/UIApplication 内部的某个位置。
因此,您还必须混合
setDelegate:
并在其中使用 class_addMethod()
,而不是稍后在委托已经设置后使用。
如果有人知道更好的方法来解决问题,请告诉我。