前言
App间跳转即App进程间的通信,有两种方式:
URL Schemes
这种方式,如果未安装,则无法通信
Universal Links
这种方式,如果未安装则会跳转到Safair,如果已安装会跳转到App
注:App间传值、App间分享、App间跳转。
URL Schemes
- 首先,创建两个项目
demoA
和 demoB
- 在
demoA
中配置 URL Schemes
,如下:
- 同理,在
demoB
中配置 URL Schemes
- 分别在
demoA
和 demoB
中配置 白名单
,即在 info.plist
中配置 Queried URL Schemes
,如下:
同理在 demoB
中配置白名单 URL Schemes:demoA
- 在
demoA
的 View Controller
中实现如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| #import "ViewController.h" #import <objc/runtime.h>
@interface ViewController () @property (nonatomic,strong)UIButton * openBtn; @end
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor whiteColor]; [self.view addSubview:self.openBtn]; }
- (void)clickBtn{ /* 注: 《demoA》: 1、需添加URL Schemes,目前为 demoA,需自定义 2、在info.plist中设置《demoB》白名单为 demoB 《demoB》: 1、需添加URL Schemes,目前为 demoB,需自定义 2、在info.plist中设置《文件保险箱》白名单为 demoA */ [self isInstallApp]; }
// 设备是否安装了《demoB》 - (void)isInstallApp{ // 可拼接任何参数 NSURL * digitalGuard_url = [NSURL URLWithString:@"demoB://from..."]; if ([[UIApplication sharedApplication] canOpenURL:digitalGuard_url]) { NSLog(@"安装了《demoB》应用"); [self openURL:digitalGuard_url]; }else { NSLog(@"未安装《demoB》应用"); } }
// 打开《demoB》 - (void)openURL:(NSURL *)url{ if (@available(iOS 10.0, *)) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) { if (success){ NSLog(@"打开《demoB》成功"); }else { NSLog(@"打开《demoB》失败"); } }];; }else { [[UIApplication sharedApplication] openURL:url];; } }
- (UIButton *)openBtn{ if (!_openBtn) { _openBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 200, 50)]; [_openBtn setTitle:@"demoB" forState:UIControlStateNormal]; [_openBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; _openBtn.backgroundColor = [UIColor redColor]; _openBtn.layer.cornerRadius = 10; [_openBtn addTarget:self action:@selector(clickBtn) forControlEvents:UIControlEventTouchUpInside]; } return _openBtn; }
@end
|
- 在
demoA
的 AppDelegate
中实现如下代码,用于接收从 demoB
中跳转过来的事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #import "AppDelegate.h" #import "ViewController.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; self.window.rootViewController = nav; [self.window makeKeyAndVisible]; return YES; }
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{ // 接收传递的参数 NSString * urlStr = url.absoluteString; // 如果是来自于《demoB》 if ([urlStr containsString:@"demoA://fromAppDemoB"]) { // 分割获取来源 NSArray * arr = [urlStr componentsSeparatedByString:@"&"]; if (arr.count > 0) { NSString * str = arr[1]; NSArray * authArr = [str componentsSeparatedByString:@"="]; if (authArr.count > 0) { NSString * authorizationCode = authArr[1]; NSLog(@"authorizationCode:%@",authorizationCode); [self showAlertWithAuthorizationCode:authorizationCode]; } } } return YES; }
- (void)showAlertWithAuthorizationCode:(NSString *)authorizationCode{ UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:authorizationCode message:nil preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction * cancleAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; UIAlertAction *confirAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; [alertVC addAction:cancleAction]; [alertVC addAction:confirAction]; [[self currentViewController] presentViewController:alertVC animated:YES completion:nil]; }
- (UIViewController *)currentViewController { UIWindow * window = [[UIApplication sharedApplication].delegate window]; UIViewController * presentedVC = [[window rootViewController] presentedViewController]; if (presentedVC) { return presentedVC; } else { return window.rootViewController; } }
@end
|
- 同理,在
demoB
的 AppDelegate
中实现一个工具类 AppCommunicate
AppCommunicate.h 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface AppCommunicate : NSObject // 单例 + (instancetype)manager;
/* 处理App间通讯 */ - (void)handleAppCommunicateWithUrl:(NSURL *)url; @end
NS_ASSUME_NONNULL_END
|
AppCommunicate.m 如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #import "AppCommunicate.h"
@interface AppCommunicate ()<NSXMLParserDelegate> @property (nonatomic,strong)NSMutableDictionary * mDic; // 保存字段 @property (nonatomic,copy)NSString * currentElementName; // 当前元素名称 @end @implementation AppCommunicate // 单例 + (instancetype)manager{ static AppCommunicate * manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [[AppCommunicate alloc] init]; }); return manager; }
/* 处理App间通讯 */ - (void)handleAppCommunicateWithUrl:(NSURL *)url{ // 接收并处理其他App传递过来的url NSString * urlStr = url.absoluteString; // 如果是来自于《demoA》 if ([urlStr containsString:@"demoB://fromAppDemoA"]) { ZJLog(@"来自于《demoA》App"); // 处理调回《demoA》 [self handleBackFileSafeWithauthorizationCode:@"zhangjian"]; } }
#pragma mark - 处理调回《demoA》 - (void)handleBackFileSafeWithauthorizationCode:(NSString *)authorizationCode{ NSString * filesafe_url = [NSString stringWithFormat:@"demoA://fromAppDemoB"]; if ([ZJ isInstallAppWithURLSchemes:filesafe_url]) { ZJLog(@"安装了《demoA》应用"); NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&authorizationCode=%@",filesafe_url,authorizationCode]]; if (@available(iOS 10.0, *)) { [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) { if (success){ NSLog(@"打开《demoA》成功"); }else { NSLog(@"打开《demoA》失败"); [SVPManager showFailureAndStatus:@"打开《demoA》失败"]; } }];; }else { [[UIApplication sharedApplication] openURL:url];; } }else { [SVPManager showFailureAndStatus:@"未安装《demoA》应用"]; } }
#pragma mark -懒加载 - (NSMutableDictionary *)mDic{ if (!_mDic) { _mDic = @{}.mutableCopy; } return _mDic; }
@end
|
- 在
demoB
的 AppDelegate
中调用
1 2 3 4 5 6 7 8 9 10
| // 处理URL - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { // 处理App叫通讯 [[AppCommunicate manager] handleAppCommunicateWithUrl:url]; return YES; }
|
- 至此,
demoA
和 demoB
之间就可以通信了。
Universal Links
什么是 Universal Links(通用链接)
Universal Links(通用链接),这是 Apple
在 iOS9.0
推出的一种能通过 HTTPS
链接直接启动手机 App
(手机有安装App的情况)的链接。当你的应用支持 Universal Links(通用链接)
在用户点击一个 通用链接
时,可以跳转到 你的网站
并获得无缝 重定向
到对应的 App
,且 不需要通过 Safari 浏览器
。如果你的应用 不支持
的话,会 在 Safari 中打开该链接
。
Universal Links(通用链接)特点
Universal Link(通用链接):看起来就是一条普通的 https
链接,当然是我们在该 链接域名根目录
配置过的一个链接,也可以在该链接中放置对应的H5页面。当用户的 点击该链接
,只要手机中 安装了支持该链接的APP就会直接进入到APP中
。如果 没有安装APP则会跳转到Safari浏览器中
,展示H5页面。对用户来说则是一个无缝跳转的过程。
使用 Universal Link(通用链接)
可以让用户在 Safari
浏览器或者其他APP的 webview中拉起相应的APP,也可以在APP中使用相应的功能,从而来把用户引流到APP中。比如淘宝当在Safari浏览器中进入淘宝网页点击打开APP则会使用Universal Link(通用链接)来拉起淘宝APP。
对比 URL Schemes,Universal Links(通用链接)的优点
灵活性:即使未安装APP,Universal Link也可以正常使用,网页可以显示跳转AppStore下载的引导,进一步地提升用户体验。
安全性:开发者在自己的网站域名配置了 apple-app-site-association
,才能通过对应的域名调起APP。另外域名必须支持 HTTPS
。
隐私性:在iOS9之前,基于URL Scheme,大家可以判断手机是否安装某APP。并且可以随便根据URL Scheme打开其他APP,像以前之前可以打开微信的扫一扫等各种功能。使用Universal Link就不会被其他恶意开发者随便跳转打开你的APP具体页面。
通用性:一个URL对你的网站和App都通用,Universal Links 是标准的URL格式,而自定义URL Scheme可能理解为特殊URL方案,默认只有你的App能解析,浏览器无法解析。
如何配置Universal Link
- 创建配置文件
客户端桌面创建一个名叫 apple-app-site-association
的文件(不需要文件后缀),包含固定格式的json文件内容如下:
1 2
| mac@bogon ~ % cd Desktop mac@bogon Desktop % touch apple-app-site-association
|
注意:文件名必须为 apple-app-site-association
,不能带后缀
1 2 3 4 5 6 7 8 9 10 11
| { "applinks": { "apps": [], "details": [ { "appID": "8LNS23U4A8.cn.epod.srsf", "paths": [ "*" ] } ] } }
|
说明:
1、appID
为 teamID.BundleID
2、paths
为 设置哪些路径可以唤起App,可以用 “*” 表示所有路径
- 在项目的
Capablities
中开启 Associated domains
,一定要按照 applinks:域名
的格式填写:
- 服务器配置
将 apple-app-site-association
文件上传到服务器,这个需要 后端/运维
配合完成,将这个 文件放到 服务器的根目录下
或者放到 .well-known
目录下。
- 验证
Universal Link
是否配置成功
如果已经配置过 Universal Link
,那么在用户 第一次安装App
时,苹果
会发送一个 请求
,请求你服务器上的 apple-app-site-association
文件
请求 apple-app-site-association
文件成功后,用户就可以使用 Universal Link
唤醒 App
了,测试的时候我们最好把App删了重新安装,确保苹果会发送请求 apple-app-site-association
文件。
- 验证
会出现两种结果,未安装App时:
已安装App时:
- 通过
Universal Link
打开 App
之后,可以根据 path
去做不同的操作,比如 打开某个特定页面等等
。