在 didFinishLaunchingWithOptions 之后几秒钟调用应用程序 openURL

Posted

技术标签:

【中文标题】在 didFinishLaunchingWithOptions 之后几秒钟调用应用程序 openURL【英文标题】:Application openURL gets called a few seconds after didFinishLaunchingWithOptions 【发布时间】: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 我也在实现相同的功能并面临同样的问题。你能帮我整理一下吗?提前致谢 【参考方案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..)打开附件 你会看到:

【讨论】:

以上是关于在 didFinishLaunchingWithOptions 之后几秒钟调用应用程序 openURL的主要内容,如果未能解决你的问题,请参考以下文章

分配的变量引用在哪里,在堆栈中还是在堆中?

NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记

秋的潇洒在啥?在啥在啥?

上传的数据在云端的怎么查看,保存在啥位置?

在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?

存储在 plist 中的数据在模拟器中有效,但在设备中无效