惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
C
Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
A
Arctic Wolf
Scott Helme
Scott Helme
P
Palo Alto Networks Blog
S
Schneier on Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
量子位
G
Google Developers Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog RSS Feed
NISL@THU
NISL@THU
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
AWS News Blog
AWS News Blog
爱范儿
爱范儿
Last Week in AI
Last Week in AI
Y
Y Combinator Blog
L
LINUX DO - 最新话题
Security Archives - TechRepublic
Security Archives - TechRepublic
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Secure Thoughts
Cloudbric
Cloudbric
aimingoo的专栏
aimingoo的专栏
L
Lohrmann on Cybersecurity
TaoSecurity Blog
TaoSecurity Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Hacker News: Ask HN
Hacker News: Ask HN
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
有赞技术团队
有赞技术团队
S
Security @ Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
G
GRAHAM CLULEY
P
Proofpoint News Feed
V
V2EX
Martin Fowler
Martin Fowler
C
CERT Recently Published Vulnerability Notes
Attack and Defense Labs
Attack and Defense Labs
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Cloudflare Blog
SecWiki News
SecWiki News
罗磊的独立博客
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
小众软件
小众软件
The Last Watchdog
The Last Watchdog

Roy的个人站

天天在网站上填材料?简单配置,让AI替你操作 | Roy的个人站 只是组件 使用手册 | Roy的个人站 IOS常用URL Scheme整理 | Roy的个人站 Anchor 专业版手册 | Roy的个人站 MacOS在NSImage里提供了哪些系统图标 | Roy的个人站 即刻寻找果果活动技术向剧透收集 | Roy的个人站 借助amis通过json构建前端页面 | Roy的个人站 浅析Pandownload作者可能面临的责任 | Roy的个人站 避免docker挂载时产生root权限文件 | Roy的个人站 一些有关幽默的理论 | Roy的个人站 手绘一个苹果手表的动图表盘 | Roy的个人站 用markdown编写man文档 | Roy的个人站 简化scp命令文件上传下载 | Roy的个人站 PPT中使用ECharts实现可拖拽关系图 | Roy的个人站 博客代码框设置换行 | Roy的个人站 畅言更名相关商标权的探究 | Roy的个人站 畅言PC版emoji显示 | Roy的个人站 畅言评论插件优化 | Roy的个人站
实现可被链接调用的IOS应用 | Roy的个人站
2020-04-10 · via Roy的个人站

我经常通过链接调用本地应用,比如支付宝的扫码支付,向特定用户发送短信。甚至一些隐藏的功能也以链接作为入口,例如Jellow现在的综合搜索。IOS上有两种实现调用的方法,分别是URL Scheme和Universal Links。本文实现了一个简单的Swift项目,实现并介绍这两种功能。

本文需要基本的Swift基础,接触过SwiftUI,大概就是官方例子大致看过的水平即可。

URL Scheme

简介

这种链接是scheme://data的形式,IOS会根据本地安装的应用搜索scheme,找到后询问你是否要通过相关应用打开该链接。很常用的一个链接是微信的扫码weixin://scanqrcode,通过这个链接直接打开微信的扫码。当然,首先需要本地安装了微信。它让类似捷径等一些自动化软件的功能大大增加。

有部分的Scheme是被保留的,可以通过这些保留的Scheme调用一些系统功能,例如短信、FaceTime,这里介绍了有哪些以及具体怎么使用这些内容,有兴趣可以尝试一下。

如果想要了解应用对应的scheme是什么,解压缩ipa,然后在Info.plist文件的CFBundleURLSchemes字段就是设置的scheme。但要了解scanqrcode之类的具体应用,就麻烦的多,网上如果没有现成的内容就需要反编译去找了。

展示收到的内容

我试了试用SwiftUI来写这个小东西,是个很简单的过程。

新建一个项目以后,在ContentView.swift里面加上基本的显示UI即可。

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
import SwiftUI

class LinkData: ObservableObject {
@Published var links :[Link]
init() {
self.links = [Link]()
}
func append(link: Link) {
self.links.append(link)
}
}

struct Link {
var time: String
var data: String
var fromApp: String
var linkType: LinkType

enum LinkType: String {
case urlScheme = "URL Scheme"
case universal = "Universal link"
}
}

struct NavigateList: View {
@ObservedObject var linkData: LinkData

var body: some View {
VStack {
NavigationView {
List{
ForEach(linkData.links, id: \.time) { link in
Row(link: link)
}
}
.navigationBarTitle("链接列表")
}
}
}
}

struct Row: View {
var link: Link

var body: some View {
VStack {
HStack {
Text(link.fromApp)
Spacer()
Text(link.time)
}
HStack {
Text(link.linkType.rawValue)
Spacer()
Text(link.data)
}
}
.padding()
}
}

struct ContentView: View {
@ObservedObject var linkData: LinkData

var body: some View {
NavigateList(linkData: linkData)
}
}

这里可以加一个按钮试一试添加是否成功,那么就把NavigateList加一点东西。

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
struct NavigateList: View {
@ObservedObject var linkData: LinkData

var body: some View {
VStack {
NavigationView {
List{
ForEach(linkData.links, id: \.time) { link in
Row(link: link)
}
}
.navigationBarTitle("Deep links")
}
Button(action: {
self.linkData.append(
link: Link(
time: "now",data: "data",
fromApp: "Demo App", linkType: .urlScheme
)
)
}) {
Image(systemName: "square.and.arrow.up")
}
}
}
}

按一下按钮,我们就假装收到了一条链接。

响应调用

应用需要决定自己响应什么scheme,这个在XCode > Project > Info > URL Types里面设置。

按照下面的图完成设置就行了。

设置Scheme

这里完成设置以后,通过测试机子的Safari,地址栏填写geoffscheme://就已经能打开应用了。

获取链接内容

这边通过修改SceneDelegate的方式接收数据,这里可能有两种情况。

不过首先,先把URL转换成显示用的Link的方法作为SceneDelegate的类方法写好。

1
2
3
4
5
6
7
8
9
10
11
func parseUrl(_ urlContext: UIOpenURLContext) -> Link {
let fromApp = urlContext.options.sourceApplication ?? "Unknown"
let url = urlContext.url.absoluteString
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyy-MM-dd HH:mm:ss"

return Link(
time: dateFormatter.string(from: Date()), data: url,
fromApp: fromApp, linkType: .urlScheme
)
}

第一种情况,如果应用没有打开,也就是后台也没有该应用,那么链接会调用scene(_:willConnectTo:options)方法。所以就给SceneDelegate添加一个属性和一个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var rootView: ContentView?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
self.rootView = ContentView(linkData: LinkData())

if let urlContext = connectionOptions.urlContexts.first {
self.rootView?.linkData.append(link: parseUrl(urlContext))
}

if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: self.rootView)
self.window = window
window.makeKeyAndVisible()
}
}

另一种情况,如果应用被打开了或者在后台,那么链接会调用scene(_:openURLContexts)方法。所以再添加一个方法。

1
2
3
4
5
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let urlContext = URLContexts.first {
self.rootView?.linkData.append(link: parseUrl(urlContext))
}
}

然后通过在Safari中键入geoffscheme://demo或者一些别的geoffscheme://开头的内容,应用都可以被正确唤起并处理链接的内容。

这种链接是https://yourwebsite的形式,它需要有一个支持HTTPS的域名并进行相关的配置。由于其安全性高,支付宝扫码支付、购买转跳等都是用的这种方式。

需要注意的是,这个方法需要一个加入苹果开发者的账号,所以这个内容还属于年费$99付费解锁。如果手头没有这样的账号的话,其实自己玩的应用URL Scheme也够用了。

设置AASA

以我的网站为例,如果我的应用要通过https://geofftools.cn调用,那么我需要提供一个这样的文件:

1
2
3
4
5
6
7
8
9
10
11
{
"applinks": {
"apps": [],
"details": [
{
"appID": "teamid.geoff.myapp",
"paths": ["*"],
}
]
}
}

这个文件可以放在两个位置,https://geofftools.cn/apple-app-site-association或者 https://geofftools.cn/.well-known/apple-app-site-association

其中Apps按要求留空(官方要求),appID是TeamId和BundleId合起来,path的路径里可以用* ? NOT

响应调用

应用需要设置响应的网站,这个在XCode > Project > Capability > Associated Domains里面设置。没有加入付费的苹果开发者的话,这就没办法设置了。

这边填<service>:<fully qualified domain>,例如applinks:geofftools.cn

获取链接内容

和上面的操作类似,这里添加这样一个类方法。

1
2
3
4
5
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if let urlContext = userActivity.webpageURL.first {
self.rootView?.linkData.append(link: parseUrl(urlContext))
}
}

对比

Universal Link更复杂也有更多的应用,URL Scheme则很容易配置也能满足基本需求,区别做成表格的话大概是这样一些不同。

Safari二次确认 需要本地安装 调用争抢 需要后端配合 需要加入开发者
Universal Link × ×
URL Scheme × ×

如果考虑安全性和易用性,肯定是Universal Link更合适,他调用应用不必须经过Safari,调用的名称也不会被其他应用占用。但如果只是做个小彩蛋,或者自己实现一些简单的功能,URL Scheme也是完全可以使用的选择。