App Clips详细解析(二) —— 一个简单示例(一)
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2020.11.18 星期三 |
前言
App Clips
是2020年WWDC新推出的功能,它的功能非常强大,因为它使没有您的应用程序的用户仍可以使用其功能。 从订购咖啡到停车,App Clips
有很多很好的用途。 下面我们就一起学习和看一下。感兴趣的可以看下面几篇文章。
1. App Clips详细解析(一) —— 基本概览(一)
开始
首先看下主要内容:
就是一起设计和实现
App Clips
,内容来自翻译。
接着看下写作环境:
Swift 5, iOS 14, Xcode 12
下面就是正文啦
在2020年苹果全球开发者大会(WWDC)
上,苹果发布了App Clips
:较小的按需版本的应用程序,允许用户执行特定任务。
App Clips
功能非常强大,因为它们使没有您的应用程序的用户仍然可以使用其功能。从订购咖啡到停车,App Clips
有很多很好的用途。更好的是,App Clips
提供了发现新应用的绝佳方法!
在本教程中,您将为SwiftyLemonade
创建一个App Clip
体验,SwiftyLemonade
是一个简单的应用程序,可让您使用App Clip
购买柠檬水。在此过程中,您将学到:
- What an App Clip is.
- How to add an App Clip target.
- How to share assets and code.
- About App Clip experiences and how to make one.
- How to confirm a user’s location using the Location Confirmation API.
- Working with App Clip notifications.
注意:本教程假定您了解
SwiftUI
的基础知识。如果您不熟悉SwiftUI
,请先查看SwiftUI: Getting Started tutorial。
注意:您必须安装
Xcode 12
才能遵循本教程。您可以在本教程的大部分内容中使用模拟器,但是要测试Location Confirmation API
,则需要一台运行iOS 14
的设备。为此,您需要先在Starter
应用中更新bundle ID
,然后才能开始按照本教程的说明进行操作。
打开入门项目。 在入门项目中,您会找到SwiftyLemonade
,该应用程序显示了各大Major League Soccer (MLS)
体育场上柠檬水摊位的列表。构建并运行以检出该应用程序:
该应用程序显示了位于各个MLS
体育场的Swifty
柠檬水摊位的列表。 您还可以将一个摊位标记为喜欢,并在单独的标签中查看收藏夹列表:
要收藏柠檬水摊位,请长按列表中的一个项目:
在这里,您可以选择柠檬水摊位,并订购Swifty
著名的柠檬水之一。 这对足球迷来说是一个真正的打击:
在Xcode中,查看将要处理的主要文件:
-
LemonadeStand.swift包含一个代表柠檬水摊位的
struct
以及要在应用程序中显示的一系列摊位。 -
Lemonade.swift包含代表柠檬水的
struct
和两个菜单数组。 - MenuList.swift显示所选柠檬水摊位的柠檬水菜单。
- DetailView.swift显示所选柠檬水的详细信息。
- StandList.swift显示一个可供选择的柠檬水摊位列表。 在这里,您可以长按收藏夹或取消收藏。
-
StandTabView.swift是一个
TabView
,用于显示柠檬水摊位或标记为收藏的柠檬水摊位的完整列表。 -
LemonadeStandLocations是一个
Swift
软件包,其中包含Swifty Lemonade
摊位的位置。
在本教程中,您将构建一个App Clip
,将您带到LA Galaxy
菜单购买柠檬水。
What Exactly Is an App Clip?
App Clip
是应用程序的轻量级版本,它使用户无需安装完整版本的应用程序即可执行特定任务。这使用户可以在需要时立即访问应用的正确部分。要启动App Clip
,请扫描NFC
标签,QR
码或App Clip
代码。此流程称为App Clip experience
。
如果用户安装了您的应用程序,则App Clip
体验将充当该应用程序的切入点。例如,Coffee
特许经营应用程序可能会具有App Clip
体验,在扫描时会转到您所在的咖啡店的菜单。或者,如果未安装该应用程序,则会从App Store
下载相关的App Clip
卡。然后将App Clip Card
呈现给用户,以启动此流程。作为开发人员,您可以使用App Store Connect
配置App Clip Card
,但请记住:它们需要一个主应用程序。
注意:如果您想了解有关配置
App Clip
的启动体验的更多信息,请查阅Apple’s documentation on Configuring Your App Clip’s Launch Experience。
Adding an App Clip Target
首先,将App Clip target
添加到项目中,并将其命名为SwiftyLemonadeClip
:
确保将Interface
设置为SwiftUI
,将Life Cycle
设置为SwiftUI App
。 然后,在出现提示时单击Activate
。 一个名为SwiftyLemonadeClip
的新组被添加到Project
导航器中:
此外,Xcode
为您的App Clip
设置名称和bundle identifier
。 您可能会注意到,bundle identifier
以.Clip
作为扩展名:
现在,您已经添加了App Clip target
,现在该进行测试了。 构建并运行:
哇! 这里没有太多的事情。 在下一部分中,您将学习如何在app
和App Clip target
之间共享代码和资源。
Sharing Assets and Code Between Targets
设置好项目后,您就可以开始从App
到App Clip
共享资源和代码了!
注意:
App Clip
和App
可以共享很多内容,但不应共享敏感信息。 如果您想了解有关将数据提供给App Clip的相应App
的信息,请查阅Apple’s Documentation。
1. Sharing Code and Assets
由于App Clips
是主App
的轻量级版本,因此将存在依赖性。 返回Xcode,单击SwiftyLemonadeClip target
,然后在Frameworks,Libraries, and Embedded Content
部分中将LemonadeStandLocations Swift
软件包添加为依赖项。 您的App Clip
现在可以访问柠檬水摊位的位置:
接下来,共享一些Swift
文件。 App Clips
将需要了解有关柠檬水摊位的信息。 在项目导航器中单击LemonadeStand.swift
,然后在文件检查器中更新target membership
,以包括SwiftyLemonadeClip
:
完成此操作后,您应该看到很多error
:
不用担心! 添加剩余的依赖项后,这些错误将消失。
就像对LemonadeStand.swift
一样,更新以下文件的target membership
:
- Lemonade.swift
- MenuList.swift
- DetailView.swift
- OrderPlacedView.swift
- StandList.swift
- Assets.xcassets in the SwiftyLemonade group
很好! 没有更多的错误!
Designing the App Clip Experience
App Clip experience
是使用URL调用应用程序的切入点。 一个应用程序可能具有许多导致特定任务的App Clip
体验URL
。 在本教程中,您将在Swifty
的一个柠檬水摊位上启动一个App Clip
体验URL
,其中显示了下订单的菜单。
首先,在SwiftyLemonadeClip
下创建一个新的Swift
文件,并将其命名为SwiftyLemonadeClipModel.swift
。 禁用SwiftyLemonade target
,因为新文件只需要对您的App Clip
可用:
然后,在SwiftyLemonadeClipModel.swift
中,在import Foundation
下添加以下代码:
class SwiftyLemonadeClipModel: ObservableObject {
@Published var selectedStand: LemonadeStand?
}
在这里,您创建了一个符合ObservableObject
的SwiftyLemonadeClipModel
类。 您还添加了@Published
属性,以将选定的摊位通知您的App Clip
。
接下来,必须在SwiftyLemonadeClipApp.swift
中实例化模型。 将以下属性添加到该结构体:
@StateObject private var model = SwiftyLemonadeClipModel()
现在,您必须将此属性提供给App Clip
的子视图。 仍在SwiftyLemonadeClipApp.swift
中,将body
替换为以下内容:
var body: some Scene {
WindowGroup {
//1
ContentView()
.environmentObject(model)
}
}
在上面的代码中,将model
设置为环境对象,使其可用于ContentView
视图子层次结构。 接下来,您将确定要选择clip
的正确摊位。
1. Getting App Clip Experience Data
数据通过App Store Connect
中的注册URL
传递到App Clip
。 注册URL不在本教程的讨论范围之内,但这并不意味着您是失败的团队! 要从URL获取数据,您必须配置App Clip
才能这样做。
首先,单击Signing & Capabilities
选项卡下的SwiftyLemonadeClip target
,然后添加一个名为appClips:swiftyLemonade.example
的新Associated Domain:
接下来,App Clip
必须解释数据。 返回SwiftyLemonadeClipApp.swift
,将body
替换为以下内容以从URL获取查询项:
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(model)
.onContinueUserActivity(
NSUserActivityTypeBrowsingWeb,
perform: handleUserActivity) //1
}
}
// 2
func handleUserActivity(_ userActivity: NSUserActivity) {
//3
guard
let incomingURL = userActivity.webpageURL,
let components = URLComponents(
url: incomingURL,
resolvingAgainstBaseURL: true),
let queryItems = components.queryItems
else {
return
}
//4
guard
let latValue = queryItems.first(where: { $0.name == "lat" })?.value,
let lonValue = queryItems.first(where: { $0.name == "lon" })?.value,
let lat = Double(latValue),
let lon = Double(lonValue)
else {
return
}
//5
print("Latitude: \(lat), Longitude: \(lon)")
}
主要做了这些:
- 1) 注册
NSUserActivityTypeBrowsingWeb
的处理程序。当iOS遇到App Clip experience URL
时,它将调用此处理程序。 - 2) 处理
URL
数据。 - 3) 仅当存在包含
queryItems
的URL
时才继续执行 - 4) 检查是否存在名为
lat
和lon
的queryItems
,并将它们分配给latValue
和lonValue
。这些项目代表关联的柠檬水摊位的经度和纬度。如果这些值不存在,则对于此App Clip
体验无效。这些值是String
类型的,您可以将它们转换为Double
类型。 - 5) 将
lat
和lon
值打印到控制台。
2. Simulating a Clip Launch
要对此进行测试,请创建启动URL
。这使您可以模拟从App Clip experience URL
启动App Clip
。要创建一个,将活动scheme
设置为SwiftyLemonadeClip
。然后,编辑scheme
并通过单击复选框启用_XCAppClipURL
环境变量。最后,将其值设置为https://swiftyLemonade.example.com/order?lat=33.8644&lon=-118.2611
,如下所示:
您添加的URL的查询参数是lat
和lon
,它们的值表示柠檬水摊位的纬度和经度。
现在,构建并运行。 您会看到纬度和经度值已打印到控制台:
3. What Lemonade Stand Is This?!
是时候找到离您最近的柠檬水摊位了。 返回SwiftyLemonadeClipApp
,在import SwiftUI
下,添加以下内容:
import CoreLocation
接下来,在handleUserActivity(_ :)
中,用以下代码替换先前添加的print()
:
//1
let location = CLLocationCoordinate2D(
latitude: CLLocationDegrees(lat),
longitude: CLLocationDegrees(lon))
//2
if let stand = standData.first(where: { $0.coordinate == location }) {
model.selectedStand = stand
//3
print("Welcome to \(stand.title)! :]")
}
这段代码:
- 1) 使用从URL获得的
lat
和lon
值创建一个CLLocationCoordinate2D
变量 - 2) 查询
standData
以查找具有匹配位置的第一个值。 如果找到位置,则将其设置为SwiftyLemonadeClipModel
中的selectedStand
。 - 3) 将
stand
名称打印到控制台
要进行检查,请构建并运行并查看打印到控制台的欢迎消息:
您已将App Clip
配置为从URL获取数据!
4. Ordering Some Lemonade
现在该为SwiftyLemonade
创建App Clip
体验了。 在本部分中,您将采用所选的stand
并显示其相关菜单。 然后,用户将能够订购一些柠檬水。
首先,在SwiftyLemonadeClip
下,打开ContentView.swift
。 在ContentView
中添加以下属性:
@EnvironmentObject private var model: SwiftyLemonadeClipModel
在这里,您已经添加了先前创建的模型作为环境对象。
接下来,将body
替换为以下内容:
var body: some View {
//1
if let selectedStand = model.selectedStand {
//2
NavigationView {
//3
MenuList(stand: selectedStand)
}
}
}
这段代码:
- 1) 检查模型是否具有
selectedStand
。 - 2) 添加以
MenuList
作为根视图的导航层次结构。 - 3) 实例化
selectedStand
的MenuList
以显示菜单项列表。
要检查这一点,构建并运行:
现在,您已将模型链接到内容视图,用户可以通过扫描Swifty's LA Galaxy Lemonade Stand
上的URL
代码订购柠檬水。 App Clip
体验应专注于特定任务,例如订购柠檬水。 因此,请注意屏幕底部缺少tab bar
,并且没有可供选择的stands
列表:
5. Can’t Find a Lemonade Stand?
如果该应用找不到某个位置的柠檬水架怎么办? 向用户显示消息会很好。 打开SwiftyLemonadeClipModel.swift
并添加以下属性:
@Published var locationFound = true
此属性跟踪应用程序是否找到柠檬水摊位。 默认情况下是true
的,因为它很可能会找到柠檬水摊位。
接下来,打开SwiftyLemonadeClipApp
,并在handleUserActivity(_ :)
中,在可选绑定代码之后添加else
子句,以找到柠檬水摊位:
else {
model.locationFound = false
}
如果该位置没有柠檬水摊位,请将locationFound
设置为false
。 您还可以根据需要删除print
语句,这仅用于调试。
现在,回到SwiftyLemonadeClip
下的ContentView.swift
,将以下内容添加到body
的末尾:
if model.locationFound == false {
Text("Error finding stand.")
}
如果附近没有摊位,则会显示一条好消息。
要对此进行测试,请更新_XCAppClipURL
以包含无效的纬度。 将值设置为https://swiftyLemonade.example.com/order?lat=33.8644&lon=0
。
构建并运行以查看错误消息:
很好!现在,您已经更新了App Clip
,以处理无效的柠檬水摊位。在继续之前,请将_XCAPPClipURL
更改回有效URL:https://swiftyLemonade.example.com/order?lat=33.8644&lon=-118.2611
。
为了结束本节,您添加了一个App Clip
体验,该体验通过使用URL作为启动参数来启动。该URL
提供您所处柠檬水摊位的位置,并显示其菜单。您可以从这里下订单。
但是,如果配送中心发生混乱,并且将错误的标签发送到错误的柠檬水摊位,该怎么办?您可能会在其他摊位订购柠檬水!或更糟糕的是,如果有人在商店放置了无效的标签进行欺诈,该怎么办?为避免这种情况,Apple
引入了一个新的轻量级Location Confirmation API
,您将在下一节中对其进行了解。
Setting up Location Confirmation
苹果与App Clip
一起,引入了Location Confirmation API
。该框架提供了足够的信息来验证调用的App Clip
是否在预期的位置。Location Confirmation API
的工作原理是将App Clip
的激活负载与用户的位置进行比较。
注意:您需要至少运行
iOS 14
的设备来测试位置确认API。在模拟器上,您只会在控制台中看到一条错误消息。
在SwiftyLemonade
中,如果您不在正确的位置,则将禁用下订单的选项。为此,请打开SwiftyLemonadeClipModel.swift
并添加以下属性:
@Published var paymentAllowed = true
此属性确定用户是否可以订购柠檬水。
要设置位置确认,请打开App Clip
的Info.plist
。 单击App Clip
键旁边的显示三角形,以显示Requests location confirmation
键。 将其值更改为YES
:
接下来,打开SwiftyLemonadeClipApp.swift
,并在import CoreLocation
下添加以下代码:
import AppClip
这使您可以访问App Clip
有效负载信息以验证用户的位置。
现在,在SwiftyLemonadeClipApp.swift
的handleUserActivity(_ :)
底部添加以下内容:
//1
guard let payload = userActivity.appClipActivationPayload else {
return
}
//2
let region = CLCircularRegion(
center: location,
radius: 500,
identifier: "stand_location"
)
//3
payload.confirmAcquired(in: region) { inRegion, error in
//4
guard error == nil else {
print(String(describing: error?.localizedDescription))
return
}
//5
DispatchQueue.main.async {
model.paymentAllowed = inRegion
}
}
这段代码:
- 1) 通过启动
App Clip
获取有效载荷信息。 如果不存在,请停止执行。 - 2) 使用URL中找到的位置创建一个具有
500
米圆形边界的区域。Core Location
使用国际单位制作为距离。 如果您不习惯使用这些单位,那么一米就不止一码了。 - 3) 检查是否在
region
内激活了App Clip
- 4) 如果有
error
,请将其记录到控制台进行调试。 - 5)
inRegion
指示是否在正确的位置调用了App Clip
。 使用此值启用或禁用付款。
注意:如果您想了解
App Clips Activation
负载,请查看关于APActivationPayload
的Apple’s Documentation。
1. Using Custom Flags
接下来,如果用户不在预期的位置,则禁用下订单的功能。 打开在SwiftyLemonade
下的Views
组中找到的DetailView.swift
。 此视图允许您订购柠檬水。 添加以下代码:
@EnvironmentObject private var model: SwiftyLemonadeClipModel
此代码使DetailView.swift
可以访问App Clip
的模型。 但是该模型在SwiftyLemonade target
中不可用。 如果选择此scheme
并尝试构建您的应用程序,则会看到以下错误:
要解决此问题,请使用Swift Compilation Flag
将此代码包装在有条件的代码中。 打开您的App Clip target
的Build Settings
,然后将自定义标志APPCLIP
添加到Debug
和Release schemes
中:
返回到DetailView.swift
,使用自定义标志将model
包装在条件中:
#if APPCLIP
@EnvironmentObject private var model: SwiftyLemonadeClipModel
#endif
现在,选择App Clip scheme
时使用条件中的代码将只编译。 编译应用程序,看看错误消失了!
接下来,App Clip
应显示一条警告,提示您付款被禁用。 为此,请在条件之外添加一个属性:
@State private var showWarningAlert = false
此属性确定是否显示警告弹窗。
2. Disabling Ordering
接下来,在placeOrder()
内部,在orderPlaced = true
之前添加以下代码:
//1
#if APPCLIP
//2
guard model.paymentAllowed else {
//3
showWarningAlert = true
return
}
#endif
这段代码:
- 1) 仅当
scheme
中存在自定义标志时,才在条件条件内执行代码。 - 2) 检查
paymentAllowed
是否为true
。 这意味着App Clip
可以下订单。 - 3) 如果不允许付款,则将
showWarningAlert
设置为true
,并且不执行placeOrder()
中的其余代码。
3. Showing an Alert
要在调用placeOrder()
时显示alert
,请在sheet
视图修饰符的右括号后添加以下代码到正文中:
//1
.alert(isPresented: $showWarningAlert) {
//2
Alert(
title: Text("Payment Disabled"),
message: Text("The QR was scanned at an invalid location."),
dismissButton: .default(Text("OK"))
)
}
这段代码:
- 1) 如果
showWarningAlert
设置为true
,则发出alert
- 2) 将
alert
配置为具有标题,消息和关闭按钮。 此alert
警告用户不允许付款。
您可以再测试一步。 除非您居住在洛杉矶银河系所在地Dignity Health Sports Park
500米范围内,否则您将需要在设备上模拟自己的位置。 这样一来,您无需在Swifty
的Lemonade Stand
附近就可以订购柠檬水。
4. Simulating Location
要模拟在洛杉矶银河体育场的身影,请在SwiftLemonadeClip
组中创建一个GPX
文件,并将其命名为LaGalaxy.gpx
。 确保选择SwiftLemonadeClip target
:
打开LaGalaxy.gpx
并将其内容替换为以下内容:
<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
<wpt lat="33.8644" lon="-118.2611">
<name>Dignity Health Sports Park</name>
<time>2014-09-24T14:55:37Z</time>
</wpt>
</gpx>
您添加的代码代表Dignity Health Sports Park
的GPS
坐标。
5. Putting it all Together
最后,要测试每种情况,在构建和运行应用程序时必须设置默认位置。 为此,您必须编辑App Clip
的scheme
并设置默认位置。
首先,在您不在预期位置的地方测试错误流。 要设置默认位置,请选择SwiftyLemonadeClip scheme
,然后选择Edit Scheme
:
现在该检查一下您添加的漂亮的错误alert
了。 构建并运行该应用程序,然后尝试下订单:
接下来是时候模拟在洛杉矶了。返回并编辑scheme
,并将默认位置设置为您先前创建的GPX
文件:
现在,在设备中设置了体育场的GPS
坐标后,您应该可以订购一些柠檬水了。 构建并运行:
恭喜你! 您已使用Location Confirmation API
来验证用户的位置并防止下错订单。
如果柠檬水准备好时能收到通知,那不是很好吗? 这样,您就可以观看比赛而不会错过任何动作。 当短暂的通知在这里时,请不要担心!
Using Ephemeral Notifications
像主应用程序一样,App Clip
可以接收通知。 通过在订单准备就绪时通知您,这些可以为App Clip
增添巨大价值。 App Clip
只能在启动后的很短时间内(最多八个小时)接收通知。
打开您的App Clip
的Info.plist
并启用临时通知-它与位置确认权限位于同一位置:
这将启用App Clip
的通知,但用户可以选择加入App Clip Card
。 结果,在SwiftyLemonadeClipApp.swift
中,在handleUserActivity(_ :)
下面添加以下代码:
func requestNotificationAuthorization() {
//1
let notifCenter = UNUserNotificationCenter.current()
notifCenter.getNotificationSettings { setting in
//2
if setting.authorizationStatus == .ephemeral {
return
}
//3
notifCenter.requestAuthorization(options: .alert) { result, error in
print("""
Authorization Request result: \(result) \
- \(String(describing: error))
""")
}
}
}
这段代码:
- 1) 检索应用程序的通知设置
- 2) 检查是否已授权该应用接收短暂通知。 如果已经授予访问权限,则无需继续。 但是,如果未授予访问权限,则再次请求它。
最后,尽管仍在SwiftyLemonadeClipApp.swift
中,将以下代码添加到body
的末尾。 确保它出现在WindowGroup
的右括号内:
//1
.onAppear {
requestNotificationAuthorization()
}
出现此视图时,此代码调用requestNotificationAuthorization()
。
启用通知后,您现在无需错过所有足球比赛。
注意:如果您想了解有关
App Clip
通知的更多信息,请查阅 Apple’s Documentation或我们的Push Notifications Tutorial。
在本教程中,您学习了如何:
- 1) Add an App Clip target
- 2) Share assets and code
- 3) Use the Location Confirmation API to verify you are at the correct location
- 4) Set up App Clip notifications
如果您喜欢本教程,请查看SwiftUI by Tutorials.。 您将深入研究如何使用简洁的声明性语言来定义应用程序的UI,以及告别大量令人困惑的UIKit
代码。
如果您想了解有关App Clip
的更多信息,请查阅 Apple’s Documentation。
后记
本篇主要讲述了
App Clips
的一个简单示例,感兴趣的给个赞或者关注~~~