URL Scheme 、Universal Link、Deep Linking

2,286 阅读4分钟

URL Scheme

URL Scheme是app跳转使用的

 

一般使用情景

App之间跳转
网页跳转App


优点
设计简单、方便


缺点
Schema 无法判断是否安装 App,如果要判断需要JS来判断(判断是否超时)
Schema 被很多 App 禁止

效果

  这里写个自定义的URL Scheme、放在备忘录里面

studyoc://www.demo.com?id=1&name=123&age=18

2639DF5A008F52CCAB35C723BFC28014.gif

 

 

URL Scheme 实现

URL Scheme设置

1.jpg

 

OC

AppDelegate 关键代码

// 热启动 冷启动 调用
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    // 因为使用了观察者,冷启动的时候,需要等初始化完成
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.50 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSString *hostUrl = url.host;
        NSDictionary *paras = [url getURLParameters];
        NSLog(@"AppDelegate scheme url = %@",hostUrl);
        NSLog(@"AppDelegate scheme 参数 = %@", paras);
        
        LinkModel *model = [LinkModel new];
        model.url = hostUrl;
        model.para = paras;
        [[NSNotificationCenter defaultCenter] postNotificationName:@"Scheme" object:model];
    });
    return YES;
}

 

SceneDelegate 关键代码

如果没有使用SceneDelegate,请忽略

// 冷启动调用
- (void)scheme:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions
{
    // 冷启动的时候,需要等初始化完成
    for (UIOpenURLContext *context in connectionOptions.URLContexts) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.50 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSString *url = context.URL.host;
            NSDictionary *paras = [context.URL getURLParameters];
            NSLog(@"SceneDelegate scheme url = %@",url);
            NSLog(@"SceneDelegate scheme 参数 = %@", paras);
            
            LinkModel *model = [LinkModel new];
            model.url = url;
            model.para = paras;
            [[NSNotificationCenter defaultCenter] postNotificationName:@"Scheme" object:model];
        });
    }
}




// 热启动调用
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
{
    NSString *url = URLContexts.allObjects.firstObject.URL.host;
    NSDictionary *paras = [URLContexts.allObjects.firstObject.URL getURLParameters];
    NSLog(@"SceneDelegate scheme url = %@",url);
    NSLog(@"SceneDelegate scheme 参数 = %@", paras);
    
    LinkModel *model = [LinkModel new];
    model.url = url;
    model.para = paras;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"Scheme" object:model];
}

 

 

Swift

AppDelegate 关键代码

    // 热启动 冷启动 调用
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        
        // 因为使用了观察者,冷启动的时候,需要等初始化完成
        DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute:{
            let hostUrl = url.host
            let paras = url.absoluteURL.urlStringtoParams(url.absoluteString)
            print("AppDelegate scheme url = (hostUrl)")
            print("AppDelegate scheme paras = (paras)")
            
            var model = LinkModel()
            var para = Para()
            model.url = hostUrl
            para.name = paras["name"] as? String
            para.id = paras["id"] as? String
            para.age = paras["age"] as? String
            model.para = para
            NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "Scheme"), object: model)
        })
        
        return true

    }

SceneDelegate 关键代码

如果没有使用SceneDelegate,请忽略

    // 冷启动调用
    func scheme(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let _ = (scene as? UIWindowScene) else { return }
        
        
        for context in connectionOptions.urlContexts{
 
            let hostUrl = context.url.host
            let paras = context.url.absoluteURL.urlStringtoParams(context.url.absoluteString)
            print("SceneDelegate scheme url = (hostUrl)")
            print("SceneDelegate scheme paras = (paras)")
            
            var model = LinkModel()
            var para = Para()
            model.url = hostUrl
            para.name = paras["name"] as? String
            para.id = paras["id"] as? String
            para.age = paras["age"] as? String
            model.para = para
            
            DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute:
                                            {
                                                NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "Scheme"), object: model)
                                            })
                
            
        }
        
        
        
    }
    
    // 热启动调用
    func scene(_ scene:UIScene, openURLContexts URLContexts:Set<UIOpenURLContext>) {
        guard let url = URLContexts.first?.url else { return }
        let hostUrl = url.host
        let paras = url.absoluteURL.urlStringtoParams(url.absoluteString)
        print("SceneDelegate scheme url = (hostUrl)")
        print("SceneDelegate scheme paras = (paras)")
        
        var model = LinkModel()
        var para = Para()
        model.url = hostUrl
        para.name = paras["name"] as? String
        para.id = paras["id"] as? String
        para.age = paras["age"] as? String
        model.para = para
        
        NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "Scheme"), object: model)
    }

 

Universal Link

Universal Link是app跳转使用的

 

一般使用情景

App之间跳转
网页跳转App


优点
直接点击不会出现弹窗、跳转方便
自动判断App有没有安装、跳转到App Store


缺点
配置麻烦 需要SSL证书、测试需要等一段时间
网络不好的情况下、第一次下载成功打开情况下是无法跳转的。原因是Universal Link需要下载apple-app-site-association文件。网络不好会导致下载失败

 

 

效果

因为要上传文件给https的服务器、效果图就使用项目的图了

C7D1D765B71BCC15D92AF086BEDDA6D0.gif

  这里写个测试的URL、放在备忘录里面

https://www.demo.com?id=1&name=123&age=18

 

Universal Link 实现

Universal Link配置

添加Universal Link

1.png   编写url 2.png

  编写apple-app-site-association文件

// appID 格式是teamid.Bundle id
// paths 同意跳转的路径
{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "teamId.cn.hahn.study.studyOC",
                "paths": [ "*" ]
            }
        ]
    },
    "webcredentials": {
       "apps": [ "teamId.cn.hahn.study.studyOC" ]
    }
}

teamId查看

4521631868497_.pic.jpg

将apple-app-site-association文件上传该文件到你的域名所对应的根目录或者.well-known目录

苹果为了方便开发者,提供了一个网页来验证我们编写的这个apple-app-site-association是否合法有效,进入验证网址进行验证:

OC

AppDelegate 关键代码

- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
    // 因为使用了观察者,冷启动的时候,需要等初始化完成
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.50 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSString *hostUrl = userActivity.webpageURL.host;
        NSDictionary *paras = [userActivity.webpageURL getURLParameters];
        NSLog(@"AppDelegate universal url = %@",hostUrl);
        NSLog(@"AppDelegate universal 参数 = %@", paras);
        
        LinkModel *model = [LinkModel new];
        model.url = hostUrl;
        model.para = paras;
        [[NSNotificationCenter defaultCenter] postNotificationName:@"Universal" object:model];
    });
    return YES;
    return YES;
}

 

SceneDelegate 关键代码

- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity API_AVAILABLE(ios(13.0))
{
  // 处理逻辑
    NSString *url = userActivity.webpageURL.host;
    NSDictionary *paras = [userActivity.webpageURL getURLParameters];
    NSLog(@"SceneDelegate universal url = %@",url);
    NSLog(@"SceneDelegate universal 参数 = %@", paras);
    
    LinkModel *model = [LinkModel new];
    model.url = url;
    model.para = paras;
    [[NSNotificationCenter defaultCenter] postNotificationName:@"Universal" object:model];
}

 

Swift

AppDelegate 关键代码

    // 热启动 冷启动 调用
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        // 因为使用了观察者,冷启动的时候,需要等初始化完成
        DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute:{
            if let url = userActivity.webpageURL {
                let hostUrl = url.host
                let paras = url.absoluteURL.urlStringtoParams(url.absoluteString)
                print("SceneDelegate universal url = (hostUrl)")
                print("SceneDelegate universal paras = (paras)")
                
                var model = LinkModel()
                var para = Para()
                model.url = hostUrl
                para.name = paras["name"] as? String
                para.id = paras["id"] as? String
                para.age = paras["age"] as? String
                model.para = para
                
                NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "Universal"), object: model)
            }
        })
        return true
    }

SceneDelegate 关键代码

    // 热启动 冷启动 调用
    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        
        DispatchQueue.main.asyncAfter(deadline: .now()+0.5, execute:{
            if let url = userActivity.webpageURL {
                let hostUrl = url.host
                let paras = url.absoluteURL.urlStringtoParams(url.absoluteString)
                print("SceneDelegate universal url = (hostUrl)")
                print("SceneDelegate universal paras = (paras)")
                
                var model = LinkModel()
                var para = Para()
                model.url = hostUrl
                para.name = paras["name"] as? String
                para.id = paras["id"] as? String
                para.age = paras["age"] as? String
                model.para = para
                
                NotificationCenter.default.post(name: NSNotification.Name.init(rawValue: "Universal"), object: model)
            }
        })
        
    }

 

Deferred Deep Linking

Deferred Deep Linking是延迟深度链接、一般使用Universal Link 或 URL Scheme在没有下载的情况只能跳转到App Store。下载完成以后Universal Link 或 URL Scheme的参数是没有办法调用的,需要重新点击。

  Deferred Deep Linking是可以保存Universal Link 或 URL Scheme的参数的技术。一般是是使用第三方的,比如Appflyer等。这些集成大平台的SDK。

有一个取巧的方法,就是在web里面弄一个下载界面。web下载按钮弄一个复制保存参数。App读取粘贴板。

 

代码

github