WWDC Quick Look 💓 By SwiftGGTeam
Streamline your App Clip

Streamline your App Clip

观看原视频

Highlight

Apple 展示了如何用位置确认、8 小时临时通知权限、SKOverlay 和 secure app group,把扫码点单这类 App Clip 交易流程从调起延续到完整 App。

核心内容

App Clip 的价值来自现场感。用户在店里扫 NFC 标签或 QR code,想做的通常只有一件事:看菜单、付款、取货。完整 App 里的账户中心、设置页、复杂导航,放进这个场景只会拖慢完成任务的速度。

这场 session 用 Fruta 点果昔流程说明问题。没有优化时,用户扫码进入 App Clip 后,还要处理定位权限、通知权限、付款、注册或登录。每一步都合理,连在一起就变成了摩擦。

Apple 给出的办法是把权限请求移到 App Clip Card 和系统能力里。位置确认只返回物理码是否在指定区域内取得,不暴露完整定位。App Clip notification 可以在每次启动后提供最长 8 小时的临时通知权限。Apple Pay 负责付款,Sign in with Apple 和 secure app group 负责把身份延续到完整 App。

这也是构建方式上的取舍。App Clip 应该只带完成当前任务所需的资源,启动后立即可用。共享资产可以放在共享 asset catalog,共享类和本地化字符串可以在 App 和 App Clip 之间复用。创建账户这类重任务放在交易完成后,或等用户安装完整 App 后再处理。

详细内容

用位置确认保护物理码交易

07:53)物理码会把用户带到某个 App Clip experience。问题是,码可能贴错门店,也可能被人恶意替换。App Clip 的位置确认 API 让应用验证“这个码是否在预期区域内被取得”,返回结果是一个 yes/no 判断。

import AppClip

guard let payload = userActivity.appClipActivationPayload else {
    return
}

let region = CLCircularRegion(center: CLLocationCoordinate2D(latitude: 37.3298193,
    longitude: -122.0071671), radius: 100, identifier: "apple_park")

payload.confirmAcquired(in: region) { (inRegion, error) in

}

关键点:

  • userActivity.appClipActivationPayload 来自 App Clip 的调起上下文。
  • CLCircularRegion 描述物理码应该出现的位置范围;session 提到半径最大可到 500 米。
  • confirmAcquired(in:) 只确认 payload 是否在该区域内取得。
  • inRegion 为 false 时,可以阻止付款或提示用户检查门店。

读取 App Clip Card 上的临时通知授权

09:24)App Clip 可以在 Card 上请求临时通知权限。用户同意后,App Clip 每次启动后最多有 8 小时通知能力,适合订单完成、取货提醒、排队状态这类短生命周期消息。

import UserNotifications

let center = UNUserNotificationCenter.current()

center.getNotificationSettings { (settings) in
   if settings.authorizationStatus == .ephemeral {
        // User has already granted ephemeral notification.
    }

}

关键点:

  • UNUserNotificationCenter.current() 读取当前通知设置。
  • .ephemeral 是 App Clip 的临时通知授权状态。
  • 已经取得临时授权时,应用不需要再弹出普通通知权限请求。
  • 对更长期的通知关系,仍然可以在合适时机请求常规通知权限。

在任务完成后展示 SKOverlay

10:49)用户完成付款后,才是引导安装完整 App 的好时机。StoreKit 的 SKOverlay.AppClipConfiguration 可以把 App Store 入口嵌入 App Clip 视图中,session 建议把它放在付款确认页附近。

import SwiftUI
import StoreKit

struct ContentView : View {
    @State private var finishedPaymentFlow = false

    var body: some View {
        NavigationView {
            CheckoutView($finishedPaymentFlow)
        }
        .appStoreOverlay(isPresented: $finishedPaymentFlow) {
            SKOverlay.AppClipConfiguration(position: .bottom)
        }
    }
}

关键点:

  • finishedPaymentFlow 表示用户是否已经完成付款流程。
  • .appStoreOverlay(isPresented:) 把 overlay 的显示时机交给 SwiftUI 状态控制。
  • SKOverlay.AppClipConfiguration(position: .bottom) 使用 App Clip 专用配置。
  • overlay 不打断下单过程,适合在确认页提示用户安装完整 App。

把登录状态迁移到完整 App

11:32)App Clip 被完整 App 取代时,可以通过 secure app group 传递少量用户数据。session 的做法是在 App Clip 内使用 Sign in with Apple,成功后把 credential.user 保存到共享 group container。

// Automatically log in with Sign in with Apple
import AuthenticationServices

SignInWithAppleButton(.signUp, onRequest: { _ in
}, onCompletion: { result in
    switch result {
    case .success(let authorization):
        guard let secureAppGroupURL =
            FileManager.default.containerURL(forSecurityApplicationGroupIdentifier:
                "group.com.example.apple-samplecode.fruta")
            else { return };
        guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential
            else { return }
        save(userID: credential.user, in: secureAppGroupURL)
    case .failure(let error):
        print(error)
   }
})

关键点:

  • SignInWithAppleButton 在 App Clip 内完成注册或登录。
  • containerURL(forSecurityApplicationGroupIdentifier:) 取得 App Clip 和完整 App 可共享的容器。
  • ASAuthorizationAppleIDCredential 提供稳定的 user 标识。
  • 这里只保存用户标识,完整 App 启动后还要再次验证凭证状态。

11:55)完整 App 首次启动后,读取同一个 group container 中的用户标识,再用 ASAuthorizationAppleIDProvider 验证凭证仍然有效。验证通过后,应用可以恢复用户的收藏、奖励或订单历史。

import AuthenticationServices

let provider = ASAuthorizationAppleIDProvider()
guard let secureAppGroupURL =
    FileManager.default.containerURL(forSecurityApplicationGroupIdentifier:
        "group.com.example.apple-samplecode.fruta")
    else { return };
let user = readUserID(in: secureAppGroupURL)
provider.getCredentialState(forUserID: user) { state, error in
    if state == .authorized {
       loadFavoriteSmoothies(userID: user)
   }
}

关键点:

  • 完整 App 读取的是 App Clip 写入的同一个 secure app group。
  • ASAuthorizationAppleIDProvider 负责检查用户标识的授权状态。
  • state == .authorized 后再加载用户相关数据。
  • 这个流程让用户从 App Clip 安装完整 App 后直接回到已登录状态。

核心启发

1. 为门店二维码加上付款前校验

做什么:在点餐、停车、租借设备等 App Clip 中,付款前确认调起码是否在合法区域内取得。

为什么值得做:session 的 Fruta 示例说明,贴错或被替换的 QR code 会把用户带到错误门店,位置确认可以在不请求完整定位权限的情况下拦截风险。

怎么开始:在 App Clip 的 Info.plist 开启位置确认,解析 activation payload,按门店配置生成 CLCircularRegion,再用 confirmAcquired(in:) 决定是否放行付款。

2. 把短生命周期消息改成 App Clip notification

做什么:为取餐、排队、临时通行、一次性预约发送 8 小时内有效的通知。

为什么值得做:用户只需要知道“果昔好了”或“车位快到期”,普通通知权限会显得过重;临时授权能覆盖这类即时任务。

怎么开始:在 App Clip Card 请求 ephemeral notification,启动后用 getNotificationSettings 检查 .ephemeral,只在需要长期关系时再请求常规通知权限。

3. 把安装完整 App 放到任务完成后

做什么:用户付款成功、领取凭证或完成预约后,用 SKOverlay 提示安装完整 App。

为什么值得做:session 明确建议 overlay 放在用户完成任务后,例如付款确认页旁边。此时用户已经获得价值,更容易接受完整 App。

怎么开始:把交易完成状态绑定到 appStoreOverlay(isPresented:),使用 SKOverlay.AppClipConfiguration(position: .bottom),避免在任务开始前打断流程。

4. 让 App Clip 登录自然迁移到完整 App

做什么:用户在 App Clip 中用 Sign in with Apple 登录后,安装完整 App 时自动恢复身份。

为什么值得做:App Clip 被完整 App 取代时,secure app group 会转移给完整 App。用户不需要再次登录,就能看到奖励、收藏或历史订单。

怎么开始:App Clip 登录成功后把 credential.user 写入 group container;完整 App 启动时读取该值,用 getCredentialState(forUserID:) 验证后再加载账户数据。

关联 Session

评论

GitHub Issues · utterances