资讯动态

iOS App Clips 实战:从扫码触发到停车缴费 Demo 完整指南

发布时间:2026/10/14 20:01:32 来源:尧图企业网站定制
你正站在柜台前手里刚接过一张印着二维码的小票。手机扫了一下没有跳转应用商店也没有提示“下载App”屏幕上直接弹出一张带商家Logo的卡片点一下选套餐、付款、走人。这张卡片就是 iOS App Clips 在真实场景里的样子。这篇文章不是官方文档的翻译而是我从“这功能到底怎么运作”到“用停车缴费 Demo 完整跑通一遍”之后沉淀下来的学习笔记。内容包括产品定位、工程结构、扫码触发链路、权限与支付等容易踩坑的地方以及最后我给自己的调试顺序建议。适合两类人看一是还在纠结“要不要接 App Clips”的开发者二是已经在接但被各种隐藏细节卡住的人。1. 先厘清 App Clips 的产品定位它不是迷你版 App而是一张“任务卡片”1.1 它和完整 App 最本质的区别没有“安装”这个概念传统的 App 使用流程是这样的搜应用商店、看评分、点下载、等进度条、注册账号、登录、找到功能、使用。这串流程里任何一步让用户犹豫转化就断了。App Clips 把中间的步骤全部砍掉。用户通过 NFC 标签、二维码、Safari 链接等方式触发后系统会在当前位置动态获取一个轻量片段一两秒内弹出带有商家图标和名称的卡片。用户点击“打开”直接进入功能界面不需要在主屏生成图标也不需要经历安装确认。这就是我理解里最核心的差异App Clips 面对的不是“我要不要装这个 App”的问题而是“我要不要马上把这个事做完”的问题。产品设计上必须尊重这种语境——用户手里可能拿着咖啡可能站在停车位旁边可能正要赶地铁他的耐心窗口非常短。1.2 能力边界写得很清楚10MB 体积与短会话苹果官方对 App Clips 的硬性约束主要有两条大家在规划功能时最好一开始就记住包体积最终下载体积必须小于 10MB。注意这里说的是压缩后的下载体积不是源码大小也不是 .app 解压后的体积。会话时长系统会限制单次会话的使用时长官方建议是把核心任务控制在几分钟内完成。超过一定时间系统可能终止进程用户下次触发时会重新开始一个新的会话。这两条限制决定了 App Clips 天生不适合做内容浏览、社区签到、复杂表单填写这类长流程任务。它更适合的是一类“高频、单目标、马上完成”的事情停车缴费、咖啡到店自取、共享设备开锁、预约签到、优惠券核销。我做停车缴费 Demo 之前最初想塞进去的功能包括“查看历史停车记录、绑定车牌、充值余额、开具发票”后来全部砍掉了。不是技术上做不出来而是用户站在闸机前他的目标只有一个把车开出去。多余的功能都是干扰。1.3 触发方式一大堆但本质上它们都在传递同一个东西URL很多人看到 App Clips 文档里一堆触发方式会有点懵NFC 标签、二维码、Safari 智能横幅、地图位置、iMessage 链接好像很复杂。实际拆开看所有触发方式背后的机制都是同一个把一个 HTTP/HTTPS URL 交给系统系统根据 URL 匹配到你预先配置好的“App Clip Experience”然后弹出卡片。触发方式用户动作底层传递内容NFC 标签手机靠近标签NDEF 消息中的 URL二维码相机扫码二维码编码的 URLSafari 链接点击网页链接常规网页 URL地图在位置页点击Apple 开发者后台绑定的 URLiMessage点击消息链接URL这个认知很重要。排障的时候与其去怀疑 NFC 标签坏了、二维码不清晰不如先确认一件事最终到达系统的那串 URL 是否和你配置的 Experience 前缀匹配。我后来所有触发问题的排查基本都是沿着这条主线走的。2. 在 Xcode 里创建 App Clip Target 后我才理解的工程边界问题2.1 新增 Target 的过程比想象中简单但有个设置千万别删在工程里选择 File → New → Target往下拉能找到 App Clip 模板。创建时 Xcode 会自动帮你做好几件事生成一个独立的 target、Bundle Identifier 默认拼接为$(APP_BUNDLE_ID).clip、自动加入主 App 的 Embed App Clips 构建阶段、设置好配套的 Info.plist。有一点刚开始容易被忽略App Clip Target 的 Build Settings 里会多一个IS_APP_CLIP YES的标志。这个标志是系统识别“这是一个 App Clip”的关键不要手滑删掉。它决定了编译产物会被打进主 App 的AppClips目录也决定了最终打包上传时的身份。签名方面App Clip 必须和主 App 使用同一个 Team ID否则后续关联域名、App Group、Keychain 共享都会出问题。我建议在创建 Target 之后先到 Signing Capabilities 里确认主 App 和 Clip 用的都是同一个开发者账号再做其他配置。2.2 主 App 与 App Clip 的容器相互独立共享数据要靠三个渠道App Clip 和主 App 虽然代码上可以复用大量内容但在系统眼里它们是两个完全独立的 App。不配置任何共享机制时它们的 UserDefaults、文件目录、Keychain 互不可见。这个“默认隔离”的设定会让第一次接触的人很意外——你会发现用户在 Clip 里登录了主 App 仍然不认识他。如果你确实需要让两个 target 共享数据常规做法有三种通过 App Group 共享 UserDefaults 和文件通过 Keychain Sharing 共享登录凭据通过服务端保存业务数据App 端只做读取我实际用的是 App Group 加服务端组合。App Group 适合存一些轻量的、非敏感的状态比如“用户是否已经在这个设备上完成引导”真正涉及账号和订单的数据还是以服务端为准App 端只做缓存。// 写入在 App Clip 里保存一个登录标记 let sharedDefaults UserDefaults(suiteName: group.com.example.parking) sharedDefaults?.set(clip-登录成功, forKey: loginState) // 读取在主 App 里检查 if let sharedDefaults UserDefaults(suiteName: group.com.example.parking), let state sharedDefaults.string(forKey: loginState) { print(主App读取到Clip状态\(state)) }注意UserDefaults(suiteName:)使用的前提是主 App 和 App Clip 都添加了 App Groups 能力并且使用同一个 group ID。否则代码不会报错但读写都是各自容器里的数据现象就是“共享了个寂寞”。2.3 我推荐一开始就把共享业务代码抽成本地 Swift Package写第一个 Demo 的时候我是把 Clip 里的业务代码直接复制一份到主 App target 里用的。结果改了一处逻辑另一个 target 忘了同步调试了半天才发现两边行为不一致。后来我把网络层、支付逻辑、模型定义全部抽到一个本地 Swift Package 里主 App 和 Clip 都依赖这个包才算真正解决问题。这里有一个工程细节值得注意App Clip 对动态库的支持非常有限官方建议不要在里面使用动态库。如果团队用的是 CocoaPods 且开启了use_frameworks!建议改成use_frameworks! :linkage :static或者改用 Swift Package Manager 并以静态库方式链接。原因很简单动态库会引入额外的依赖解析和启动时间对 10MB 压缩包限制和秒开体验都不友好。使用 Swift Package 的好处还有一点依赖关系变得更清晰。主 App 依赖Core和MainUIClip 依赖Core和ClipUI这样就不会出现 Clip 里误编译了一大堆主 App 才需要用到的控制器。3. 用一个停车缴费 Demo 走通扫码触发的完整链路3.1 关联域名配置AASA 文件才是触发的地基扫码后系统能不能正确识别你的 URL第一步取决于关联域名Associated Domains是否配置正确。主 App 和 App Clip 都要在 Signing Capabilities 里添加applinks:你的域名然后在域名服务器根目录或.well-known路径下放一个apple-app-site-association文件。这个文件的核心作用是告诉系统这个域名下的哪些路径属于你的 App。里面的 appID 需要按“TeamID.BundleID”的格式填写且建议把主 App 和 App Clip 的 Bundle ID 都配置进去。{ applinks: { apps: [], details: [ { appID: TEAMID.com.example.parking, paths: [/clip/*] }, { appID: TEAMID.com.example.parking.clip, paths: [/clip/*] } ] } }配置完成后先用浏览器访问一下https://你的域名/.well-known/apple-app-site-association确认能正常返回 JSON 且没有跳转重定向。这一步只要卡住后面扫码就一定起不来。3.2 App Store Connect 里创建一个 Experience本地工程配置好之后还要到 App Store Connect 的后台里创建一个 App Clip Experience把触发 URL 和具体的 App Clip 版本绑定起来。需要填写的核心内容是Experience 名称显示在卡片上的文字、图标、副标题以及一个 URL 前缀。这个 URL 前缀是匹配规则我建议把不同场景用路径区分开。比如停车项目用一个体验路径前缀是/clip/parking点餐体验用另一个体验路径前缀是/clip/order。不要所有场景共用一个前缀否则后面想针对不同场景做统计和参数处理会非常麻烦。这里有个新手容易忽略的点创建 Experience 之后配置不是立即生效的。系统需要在苹果服务器上同步这些信息有时候扫二维码仍然打开 Safari等一段时间再试往往就好了。3.3 真机扫码验证从“跳到 Safari”到“弹出卡片”的排障过程我把这段排障过程记录下来是因为它几乎覆盖了新手会遇到的各类情况现象最可能原因检查点扫码后完全没反应域名关联未配置或 AASA 文件不可访问浏览器直接访问 AASA 地址检查返回 JSON扫码后跳转 Safari 而不是弹出卡片Experience 未创建、未生效或 URL 与配置前缀不匹配App Store Connect 后台检查 Experience 状态和 URL弹出卡片但内容和预期不符匹配到了错误的 Experience检查使用的是哪个 URL 前缀是否正确区分场景真机调式时点击卡片打不开App Clip target 没有安装或签名失效确认 Xcode 运行的是 Clip target且签名 Team 一致我自己遇到过最隐蔽的问题是 AASA 文件路径。当时我把文件放到了域名的根目录但项目里配置的 applinks 是example.com而 AASA 必须能通过https://example.com/.well-known/apple-app-site-association访问。后来我用了一个对比工具发现虽然根目录也能读到但系统更规范的做法是放在.well-known路径下。3.4 NFC 标签的写入没那么玄但要选对格式二维码方案验证通过后我又用普通 NFC 标签做了验证。不需要申请苹果官方的 App Clip Code直接买支持 NDEF 格式的空白标签用手机上的 NFC 写入工具把 URL 写进 NDEF 消息然后靠近手机顶部感应区。注意不要让标签里的 NDEF 消息携带自定义的协议格式必须是一个标准的 HTTP/HTTPS URL。NFC 触发和二维码触发最终走的是同一条匹配链路所以标签本身只是一个“URL 搬运工”不要把业务参数硬编码在标签里。标签里只需要带上场景标识比如https://example.com/clip/parking?spotA12其余信息尽量在打开 Clip 后通过服务端获取。4. 测试途中逐一踩过的坑权限、体积、支付一个都不少4.1 权限弹窗不能一上来就弹App Clip 是一个“用完即走”的轻量会话但它依然可以通过系统弹窗请求通知、定位、相机等权限。问题在于用户还没有理解自己打开的是什么弹窗就糊上脸他会本能地点击“不允许”。我刚开始做的版本扫码进来第一件事就是请求通知权限。后来看数据允许率非常低而且大量用户在这个弹窗出现后就关闭了卡片。调整方案是把权限请求绑定到“用户已经完成某个明确动作之后”比如停车缴费完成后再询问“需要缴费提醒吗”。这时候用户已经获得了价值对 App Clip 的信任度也有所提升授权意愿会好很多。另一个需要注意的细节是App Clip 的通知权限和主 App 是独立的。用户在 Clip 里点了允许不代表主 App 也允许反过来也一样。所以不要假设“用户之前允许过主 App 通知Clip 就可以直接发通知”。4.2 场景参数的传递从 NSUserActivity 里解析 URL用户通过扫码或 NFC 触发时App Clip 需要通过continue userActivity拿到本次调用的上下文。参数如何设计直接影响后面的业务逻辑。func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { guard let url userActivity.webpageURL else { return } let components URLComponents(url: url, resolvingAgainstBaseURL: false) let spotId components?.queryItems?.first(where: { $0.name spot })?.value print(触发场景参数 spotId\(spotId ?? )) }这里要记住一个原则URL 里的参数是不可信的。二维码可以被复制NFC 标签可以被重写所以 URL 里只放“标识类”信息比如车位号、门店 ID绝对不要放金额、优惠折扣这类需要校验的数据。真实的价格和订单信息必须等 Clip 打开后通过服务端接口获取。4.3 包里塞了什么决定了你离 10MB 有多远10MB 的限制听起来不算小但真机跑起来就会发现体积像滚雪球一样涨。最常见的超限原因是把主 App 的资源文件误拖进了 App Clip target 的 Copy Bundle Resources 阶段其次是图片、字体、本地化文件没有压缩。我在第一次打包时把一套用于主 App 引导页的插画资源放进了 Clip target体积直接上涨近 2MB。最后把这些资源移到服务端通过 URL 加载才把安装包控制在 6MB 左右。检查最终体积的标准做法是Xcode 里 Archive 之后在 Organizer 中查看 “App Size Details”它会按不同设备类型展示 App Clip 的预估下载体积。不要只看 Debug 模式下的报告那个和实际发布体积差距很大。4.4 StoreKit 在 App Clip 里表现不一样的地方做停车缴费 Demo支付是绕不开的。App Clip 的支付底层同样是 StoreKit代码写法和主 App 基本一致但我在测试中发现两个不太一样的地方。第一支付弹窗和其他系统弹窗同时出现时处理顺序要格外小心。如果用户在支付确认的同时又被请求了通知权限两个弹窗叠加极容易出现误触。我后来的处理是把所有系统弹窗串行化先完成支付回调成功后再请求通知绝不同时出现两个弹窗。第二支付回调要尽早挂上队列观察者。如果在 Clip 的入口处没有注册SKPaymentQueue的观察者而只是等用户点“支付”时才注册可能漏掉支付进程已经回调的状态。稳妥做法是在 App Clip 启动时就注册观察者并且在应用进入后台或终止前保持监听。另外一个经验是沙盒测试账号在 App Clip 和主 App 之间是共享的。如果你在测试主 App 和 Clip 各自跑同一笔订单会发现测试记录混在一起。建议测试 Clip 支付时用独立的沙盒账号避免和主 App 的验证流程互相干扰。5. 会话结束之后共享数据与引导下载完整 App 的取舍5.1 用 SKOverlay 做“温和”的拉新引导App Clips 的天然弱点是做不了长期留存。用户完成一次任务后关闭卡片下次可能再也不会主动想起这个入口。如果你想引导用户下载完整 AppApple 提供了 SKOverlay它会在 App Clip 界面顶部弹出一个小型卡片展示完整 App 的下载入口但不会打断当前操作流程。我把 SKOverlay 放在用户支付成功的页面上用户看到“下载完整 App 可以查看历史记录”有兴趣就点没兴趣就继续完成支付后的流程。相比弹窗式引导这种方式对会话的干扰最小。需要记住的是不要在每个页面都展示 SKOverlay。它本质上是一种转化工具频繁出现只会让用户反感。最好是选择一个“用户已经获得价值”的时刻比如支付完成、核销成功这时候推荐是顺理成章的。5.2 共享容器的数据同步粒度别指望实时App Group 能共享 UserDefaults 和文件但它的同步不是实时的。App Clip 写入数据后主 App 下次启动时读取拿到的是上一次写入的值反过来也一样。如果你需要“Clip 完成订单主 App 立刻能查到这个订单”那就必须通过服务端而不是共享容器。这个点我在初期理解不足曾经尝试用 App Group 的 UserDefaults 做订单状态同步结果发现两边读取经常不一致。后来我在关键动作完成时写共享容器业务数据仍然依赖服务端查询问题就解决了。5.3 什么时候不该用 App Clips把功能做得越多越容易偏离 App Clips 的定位。我总结了一张适合与不适合的对比表帮助自己判断适合用 App Clips不适合用 App Clips停车缴费、点单取餐、扫码开锁资讯阅读、视频浏览、社区签到单次交易几分钟内完成需要长期账号体系、会员等级线下场景触达用户有明确意图线上功能单纯为了减少下载步骤用完即走不依赖长期留存需要“日活”和“留存”作为核心指标我砍掉停车缴费 Demo 里“历史记录”功能的时候才发现用户真正需要的是一个“从扫码到抬杆”的最短路径。任何让这个路径变长、变复杂的设计都不适合放进 App Clip。6. 调试方法与我建议的学习顺序6.1 模拟器够用但完整链路必须真机App Clip 的界面逻辑完全可以在模拟器里调试但涉及到 NFC、二维码、关联域名匹配这些部分模拟器是验证不了的。我建议至少准备一台真机配合一个二维码就能走通最基本的端到端流程。调试的时候不需要老是写 NFC 标签直接把二维码截图存到相册用相机扫描就能触发。NFC 标签主要留到最终交付前做一轮完整的实机验证。6.2 用 Xcode Scheme 预置 Experience 的小技巧在 Xcode 里编辑 App Clip target 的 Scheme可以在 Run 配置中找到 App Clip Experiences 列表。这里可以预置不同的 Experience每次运行时会自动带入对应的 URL。这个功能对调试场景参数特别有用。我配置了两个 Experience一个模拟正常停车入口一个模拟预约入口。切换 Scheme 就能测试不同 URL 下的参数解析逻辑不需要每次都生成新的二维码。6.3 我建议的学习顺序如果完全是从零开始接触 App Clips我的建议是按这个顺序走先不写任何代码把苹果官方概览页面读一遍理解 10MB、短会话、任务导向这几个核心概念。用空工程创建一个 App Clip target跑通最基本的“点击卡片打开界面”。配置关联域名和 AASA用二维码做一次端到端验证。这是最容易卡住、也最需要耐心的环节。加入 App Group、权限请求、支付等功能逐步测试边界情况。最后再思考产品层面这个流程是否真的适合 App Clips。最后再说一点自己的体会。App Clips 的工程难度并不高真正的难度是产品判断。我做完这个 Demo 之后最大的感受是技术上的问题都能查文档解决但“用户扫码之后到底要完成什么、每一步用时多少、什么时候该退出”这些问题才是决定一个 Clip 体验好坏的关键。如果你正准备接 App Clips建议先把这段使用路径写在纸上理清用户从扫码到离开的每一步再开始写代码。这个过程比我预想的更有价值。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑