OC学习51:App之间相互跳转

张建 lol

前言

App间跳转即App进程间的通信,有两种方式:

  1. URL Schemes

这种方式,如果未安装,则无法通信

  1. Universal Links

这种方式,如果未安装则会跳转到Safair,如果已安装会跳转到App

注:App间传值、App间分享、App间跳转。

URL Schemes

  1. 首先,创建两个项目 demoAdemoB
  • demoA 中配置 URL Schemes,如下:

  • 同理,在 demoB 中配置 URL Schemes
  1. 分别在 demoAdemoB 中配置 白名单,即在 info.plist 中配置 Queried URL Schemes,如下:

同理在 demoB 中配置白名单 URL Schemes:demoA

  1. demoAView 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
  • demoAAppDelegate 中实现如下代码,用于接收从 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
  1. 同理,在 demoBAppDelegate 中实现一个工具类 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

  • demoBAppDelegate 中调用
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;
}

  1. 至此,demoAdemoB 之间就可以通信了。

Universal Links

什么是 Universal Links(通用链接)

  • Universal Links(通用链接),这是 AppleiOS9.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能解析,浏览器无法解析。

  1. 创建配置文件

客户端桌面创建一个名叫 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、appIDteamID.BundleID
2、paths 为 设置哪些路径可以唤起App,可以用 “*” 表示所有路径

  1. 在项目的 Capablities 中开启 Associated domains,一定要按照 applinks:域名 的格式填写:

  1. 服务器配置

apple-app-site-association 文件上传到服务器,这个需要 后端/运维 配合完成,将这个 文件放到 服务器的根目录下 或者放到 .well-known 目录下。

  1. 验证 Universal Link 是否配置成功
  • 如果已经配置过 Universal Link,那么在用户 第一次安装App 时,苹果 会发送一个 请求,请求你服务器上的 apple-app-site-association 文件

  • 请求 apple-app-site-association 文件成功后,用户就可以使用 Universal Link 唤醒 App 了,测试的时候我们最好把App删了重新安装,确保苹果会发送请求 apple-app-site-association 文件。

  1. 验证
  • 方式一:直接在 Safari 中输入 https://id.epod.cn 打开

  • 方式二:在 备忘录 中输入 https://id.epod.cn,然后点击打开

会出现两种结果,未安装App时:

已安装App时:

  1. 通过 Universal Link 打开 App 之后,可以根据 path 去做不同的操作,比如 打开某个特定页面等等
  • Post title:OC学习51:App之间相互跳转
  • Post author:张建
  • Create time:2023-05-25 21:28:55
  • Post link:https://redefine.ohevan.com/2023/05/25/OC/OC学习52:App之间相互跳转/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.