无法通过单击 URL 方案深层链接来调用 swizzling AppDelegate 实例方法 application(_:open:options:)

问题描述 投票:0回答:1

我使用完全相同的方法来混合两个

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 方案链接后获得预期的行为?

ios swift
1个回答
0
投票

好吧,4 1/2 年的答案已经很晚了,但至少我确切地知道问题是什么。

当 iOS 将 ApplicationDelegate 加载到

UIApplication.sharedApplication.delegate
时,它会立即检查该对象是否响应某些选择器,并将结果保留在 Application 对象中的某个位置。像这样的东西:

BOOL thisAppHandlesOpenByUrl = [delegate respondsToSelector:@selector(application:openURL:options:)];
...

这些检查是在 UIApplication 的

setDelegate:
中完成的,以后无法更改,因为状态保存在 UIKit/UIApplication 内部的某个位置。

因此,您还必须混合

setDelegate:
并在其中使用
class_addMethod()
,而不是稍后在委托已经设置后使用。

如果有人知道更好的方法来解决问题,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.