【问题标题】:Application openURL gets called a few seconds after didFinishLaunchingWithOptions在 didFinishLaunchingWithOptions 之后几秒钟调用应用程序 openURL
【发布时间】:2017-02-20 13:16:33
【问题描述】:

我的应用程序中有一个深层链接功能,在一种情况下运行良好。 根据打开应用程序的 url,我有 3 个不同的入职页面。 因此,当应用程序启动时,我需要知道哪个链接(如果有)打开了应用程序,然后显示正确的入职页面。问题是我需要知道在方法中显示什么屏幕:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 

但我只能知道深层链接是否打开了应用程序

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation 

在调用didFinishLaunchingWithOptions 5 秒后被调用(我计算了秒数)。所以我有 5 秒的时间看到错误的入职页面,直到调用 openURL(如果它会被调用)。

所以我的问题是:有什么方法可以知道应用程序是在didFinishLaunchingWithOptions 之前还是期间从 URL 启动的?

顺便说一句,当应用从深层链接打开时,didFinishLaunchingWithOptions 中的 launchOptions 为零

【问题讨论】:

  • 你好 aviv_eik 我也在实现相同的功能并面临同样的问题。你能帮我整理一下吗?提前致谢

标签: ios deep-linking uiapplicationdelegate


【解决方案1】:

您正在寻找的启动选项键是 UIApplicationLaunchOptionsURLKey (Objective-C) / UIApplicationLaunchOptionsKey.url (Swift)。
如果您的目标是 iOS 9 及更高版本,您只需拦截来自

的启动 URL
  • application:didFinishLaunchingWithOptions:(如果应用程序尚未在内存中)
  • application:openURL:options:(如果应用程序已经在后台)。

这是UIApplicationDelegate 的简约实现,应该涵盖这两种情况 - 请注意,为了清楚起见,已省略了许多不相关的逻辑:

目标-C:

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    NSURL *url = launchOptions[UIApplicationLaunchOptionsURLKey];
    if (url) {
        // TODO: handle URL from here
    }

    return YES;
}

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {

    // TODO: handle URL from here

    return YES;
}

@end

斯威夫特 5:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        if let url = launchOptions?[.url] as? URL {
            // TODO: handle URL from here
        }

        return true
    }

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {

        // TODO: handle URL from here

        return true
    }
}

【讨论】:

  • 当我从深度链接打开我的应用程序时,didFinishLaunchingWithOptions 中的“launchOptions”为零,但“openURL”确实被触发(使用深度链接 url)。我确实在“didFinishLaunchingWithOptions”中显示了我的入职页面,你认为我应该在哪里显示应用程序启动时的第一个关键窗口
  • 好吧,让我问一些其他问题:您是从网络浏览器打开您的应用程序吗?您的目标是什么最低 iOS 版本?注意 application:didFinishLaunchingWithOptions: 每次运行时只调用一次。如果您的应用程序已经在后台,则 application:openURL:options: 将被调用。我对窗口设置的评论仅适用于您将情节提要作为“主界面”设置的情况。如果你是从 application:didFinishLaunchingWithOptions: 自己设置界面,你会没事的,请忽略评论。
  • 为了清晰起见,我改进了我的答案 - 它已经在运行 iOS 9.3.4 和 iOS 10.3 的设备上使用 Xcode 8.3 进行了测试,适用于 Objective-C 和 Swift 构建。确保您确实在您的Info.plistURL Types > URL Schemes 中设置了一个值(例如“my-app”),在您的设备上构建应用程序然后杀死它,然后尝试打开例如任何网络浏览器中的“my-app://”。
  • 虽然在我编写 cmets 时这是一个老问题,但仍然想警告新读者,没有必要同时处理 application:didFinishLaunchingWithOptions:application(_:open:options:) 中的 URL。只需检查application(_:open:options:) 就足够了。 developer.apple.com/documentation/uikit/uiapplicationdelegate#//…
  • 是的。 @KutayDemireren 似乎是对的 - 即使应用程序之前被杀死,也会调用 application(_:open:options:)
【解决方案2】:

我刚刚在 iOS 13 中遇到了类似的问题,但在 iOS 13 中情况发生了变化,因为引入了 UIWindowSceneDelegate 并且现在可以完成之前由 UIApplicationDelegate 完成的一些工作(取决于您的应用程序设置)。

@Olivier 在这个线程中的answer 对我来说仍然非常有用,因为它指出了处理 URL 方案的两种情况;即当应用程序尚未在内存中时,调用application:didFinishLaunchingWithOptions:,当应用程序已经加载并在后台时,第二种情况调用application:openURL:options:

所以,正如我在上面提到的,如果您使用 XCode 11 生成的默认应用程序模板,自 iOS 13 以来情况会有所不同。我不会在这里详细介绍,所以这里有一个关于题目:Understanding the iOS 13 Scene Delegate.

但是,如果您对场景使用新方法,则修改的关键方法是scene(_:willConnectTo:options:)(文档here)和scene(_:openURLContexts:)(文档here)。前者是在应用程序尚未加载时对 URL 方案进行操作的位置(因此它有点取代 application:didFinishLaunchingWithOptions:),而后者是当应用程序已经在后台时获取 URL 的位置,而 URL 方案是调用(所以这个替换了application:openURL:options:)。

使用scene(_:willConnectTo:options:),您可以执行以下操作来查找 URL 方案的 URL(如果有):

if let url = connectionOptions.urlContexts.first?.url {
    // handle
}

对于scene(_:openURLContexts:),您可以查看URLContexts 集合的内部。

我希望这会有所帮助!

【讨论】:

    【解决方案3】:

    以前的好主意。 我做了一些测试..

    我确认 iOS 13 的几率和错误。

    序言: 我启用了 plist 上的所有标志: (来自https://forums.developer.apple.com/thread/118932

    ... UIFileSharingEnabled LSSupportsOpeningDocumentsInPlace UISupportsDocumentBrowser .. 并在 plist 中添加了所有类型:

    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeIconFiles</key>
            <array/>
            <key>CFBundleTypeName</key>
            <string>abc File</string>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>LSHandlerRank</key>
            <string>Owner</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>org.example.app.document.abc</string>
            </array>
        </dict>
    </array>
    
    
    <key>UTExportedTypeDeclarations</key>
        <array>
            <dict>
                <key>UTTypeConformsTo</key>
                <array>
                    <string>public.data</string>
                </array>
                <key>UTTypeDescription</key>
                <string>abc File</string>
                <key>UTTypeIconFiles</key>
                <array/>
                <key>UTTypeIdentifier</key>
                <string>org.example.app.document.abc</string>
                <key>UTTypeTagSpecification</key>
                <dict>
                    <key>public.filename-extension</key>
                    <array>
                        <string>abc</string>
                    </array>
                </dict>
            </dict>
        </array>
    

    我在这里登录:

    1)

      func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            // Override point for customization after application launch.
    
            print(documentsDir())
    
            if let url = launchOptions?[.url] as? URL {
                // TODO: handle URL from here
                openWriteAndCloseLog(msg: "1 " + url.absoluteString, withTimestamp: true)
            }
            return true
        }
    

    2)

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
        // TODO: handle URL from here
        openWriteAndCloseLog(msg: "2 " + url.absoluteString, withTimestamp: true)
        return true
    }
    

    3)

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 
        if let url = connectionOptions.urlContexts.first?.url {
            // handle
            openWriteAndCloseLog(msg: "3 " + url.absoluteString, withTimestamp: true)            
        }
        guard let _ = (scene as? UIWindowScene) else { return }
    }
    

    似乎我们只通过了 3(根据我的调试日志,我可以看到内部文档,正如我通过 iTunes 共享的那样)

    我做了一个小演示应用来测试它。

    https://github.com/ingconti/DocumentBroswerSampleApp

    您可以从(例如,mal..)打开附件 你会看到:

    【讨论】:

      猜你喜欢
      • 2016-08-26
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多