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,现在该进行测试了。 构建并运行:

哇! 这里没有太多的事情。 在下一部分中,您将学习如何在appApp Clip target之间共享代码和资源。


Sharing Assets and Code Between Targets

设置好项目后,您就可以开始从AppApp Clip共享资源和代码了!

注意:App ClipApp可以共享很多内容,但不应共享敏感信息。 如果您想了解有关将数据提供给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?
}

在这里,您创建了一个符合ObservableObjectSwiftyLemonadeClipModel类。 您还添加了@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) 仅当存在包含queryItemsURL时才继续执行
  • 4) 检查是否存在名为latlonqueryItems,并将它们分配给latValuelonValue。这些项目代表关联的柠檬水摊位的经度和纬度。如果这些值不存在,则对于此App Clip体验无效。这些值是String类型的,您可以将它们转换为Double类型。
  • 5) 将latlon值打印到控制台。

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的查询参数是latlon,它们的值表示柠檬水摊位的纬度和经度。

现在,构建并运行。 您会看到纬度和经度值已打印到控制台:

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获得的latlon值创建一个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) 实例化selectedStandMenuList以显示菜单项列表。

要检查这一点,构建并运行:

现在,您已将模型链接到内容视图,用户可以通过扫描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 ClipInfo.plist。 单击App Clip键旁边的显示三角形,以显示Requests location confirmation键。 将其值更改为YES

接下来,打开SwiftyLemonadeClipApp.swift,并在import CoreLocation下添加以下代码:

import AppClip

这使您可以访问App Clip有效负载信息以验证用户的位置。

现在,在SwiftyLemonadeClipApp.swifthandleUserActivity(_ :)底部添加以下内容:

//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 targetBuild Settings,然后将自定义标志APPCLIP添加到DebugRelease 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米范围内,否则您将需要在设备上模拟自己的位置。 这样一来,您无需在SwiftyLemonade Stand附近就可以订购柠檬水。

4. Simulating Location

要模拟在洛杉矶银河体育场的身影,请在SwiftLemonadeClip组中创建一个GPX文件,并将其命名为LaGalaxy.gpx。 确保选择SwiftLemonadeClip target

打开LaGalaxy.gpx并将其内容替换为以下内容:



    
        Dignity Health Sports Park
        
    

您添加的代码代表Dignity Health Sports ParkGPS坐标。

5. Putting it all Together

最后,要测试每种情况,在构建和运行应用程序时必须设置默认位置。 为此,您必须编辑App Clipscheme并设置默认位置。

首先,在您不在预期位置的地方测试错误流。 要设置默认位置,请选择SwiftyLemonadeClip scheme,然后选择Edit Scheme

现在该检查一下您添加的漂亮的错误alert了。 构建并运行该应用程序,然后尝试下订单:

接下来是时候模拟在洛杉矶了。返回并编辑scheme,并将默认位置设置为您先前创建的GPX文件:

现在,在设备中设置了体育场的GPS坐标后,您应该可以订购一些柠檬水了。 构建并运行:

恭喜你! 您已使用Location Confirmation API来验证用户的位置并防止下错订单。

如果柠檬水准备好时能收到通知,那不是很好吗? 这样,您就可以观看比赛而不会错过任何动作。 当短暂的通知在这里时,请不要担心!


Using Ephemeral Notifications

像主应用程序一样,App Clip可以接收通知。 通过在订单准备就绪时通知您,这些可以为App Clip增添巨大价值。 App Clip只能在启动后的很短时间内(最多八个小时)接收通知。

打开您的App ClipInfo.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的一个简单示例,感兴趣的给个赞或者关注~~~

你可能感兴趣的:(App Clips详细解析(二) —— 一个简单示例(一))