WWDC Quick Look 💓 By SwiftGGTeam
Day two recap

Day two recap

观看原视频

Highlight

WWDC21 第二天集中展示了声音识别、ShazamKit 自定义音频匹配、Screen Time API、StoreKit 2 和 Nearby Interaction 配件支持,开发者可以把系统级识别、限制、购买和空间感知能力接入自己的 App。

核心内容

第二天的 recap 只有一分多钟。它没有展开讲代码,而是把当天的几个技术方向串在一起。

这些方向都来自真实的开发麻烦。

你想识别环境里的声音,以前要自己训练模型、处理音频窗口、判断置信度。Sound Analysis 给了内置分类器,可以直接识别三百多种声音。

你想让手机 App 跟电视上的视频同步,以前要依赖网络、时间码或手动触发。ShazamKit 让 App 听到音频后匹配自定义目录,再根据匹配偏移显示内容。

你想做家长控制,以前拿不到 Screen Time 的系统限制能力。Screen Time API 把限制、授权和设备活动监控拆成三个框架,让 App 在隐私边界内工作。

你想做订阅,以前要处理 SKProductsRequest、交易状态和收据验证。StoreKit 2 把商品、购买、交易和权益变成 Swift 原生 API。

你想让手机和配件互相感知位置,以前 Nearby Interaction 主要用于 Apple 设备之间。iOS 15 开始支持兼容的第三方 UWB 配件。

这场 recap 的价值在于指路。它告诉你第二天哪些 session 值得继续看,以及每个新能力能解决哪类产品问题。

详细内容

用 Sound Analysis 识别环境声音

00:07

recap 提到,开发者学习了如何分析和分类声音。对应 session 是 Discover built-in sound classification in SoundAnalysis

Sound Analysis(声音分析)提供内置声音分类器。它可以从麦克风、音频文件或视频文件中识别离散声音。开发者无需先训练自定义模型。

func getListOfRecognizedSounds() throws -> [String] {
    let request = try SNClassifySoundRequest(classifierIdentifier: .version1)
    return request.knownClassifications
}

关键点:

  • getListOfRecognizedSounds() 返回内置分类器支持的声音标签。
  • SNClassifySoundRequest 是声音分类请求。
  • .version1 指定使用系统提供的第一版内置分类器。
  • knownClassifications 暴露分类器能识别的标签列表。

拿到标签后,就可以对音频文件做分类。

let request = try SNClassifySoundRequest(classifierIdentifier: .version1)

let analyzer = try SNAudioFileAnalyzer(url: url)

var observer: SNResultsObserving // TODO

try analyzer.add(request, withObserver: observer)
analyzer.analyze()

关键点:

  • 第一行创建同一个内置分类请求。
  • SNAudioFileAnalyzer(url:) 读取本地音频文件。
  • SNResultsObserving 接收分类结果。
  • analyzer.add 把请求和观察者绑定到分析器。
  • analyzer.analyze() 开始执行分析。

用 ShazamKit 做自定义音频匹配

00:17

recap 提到 ShazamKit 可以识别自定义音频。它给出的场景很具体:电视播放视频,手机上的 companion app 在正确时间显示问题。

对应 session 里,Apple 用一个教育视频做例子。App 先把视频音频生成 reference signature(参考签名),放进自定义目录。运行时,App 从麦克风采集音频流,匹配目录,然后根据 predictedCurrentMatchOffset 找到当前视频位置。

// Matching signatures using SHSession
let session = SHSession()
session.delegate = self

let signatureGenerator = SHSignatureGenerator()
try signatureGenerator.append(buffer, at: nil)

let signature = signatureGenerator.signature()
session.match(signature)

关键点:

  • SHSession 负责执行匹配。
  • session.delegate = self 让当前对象接收匹配结果。
  • SHSignatureGenerator 把音频 buffer 转成签名。
  • append(buffer, at:) 把捕获到的音频写入签名生成器。
  • signature() 生成可匹配的音频签名。
  • session.match(signature) 用该签名发起匹配。

自定义目录还可以携带元数据。匹配成功后,delegate 返回 SHMatch,App 从 mediaItems 读取标题、老师、集数等信息。

// Receiving matches via the session delegate
extension SongResultViewController: SHSessionDelegate {

    public func session(_ session: SHSession, didFind match: SHMatch) {

        guard let matchedMediaItem = match.mediaItems.first else {
            return
        }

        DispatchQueue.main.async {
            self.songView.titleLabel.text = matchedMediaItem.title
            self.songView.artistLabel.text = matchedMediaItem.artist
        }

    }
}

关键点:

  • SHSessionDelegate 接收 ShazamKit 的匹配回调。
  • session(_:didFind:) 在找到匹配时触发。
  • match.mediaItems.first 取出第一个匹配的媒体项。
  • matchedMediaItem.titleartist 是目录中保存的元数据。
  • DispatchQueue.main.async 确保界面更新发生在主线程。

用 Screen Time API 构建家长控制

00:37

recap 提到 Nolan 演示了 Screen Time API。demo App 叫 Homework:孩子需要先使用指定学习 App,才能解除某些 App 的限制。

Screen Time API 由三个框架组成。

Managed Settings(托管设置)负责设置限制,比如屏蔽应用、过滤网页、锁定账号设置。Family Controls(家庭控制)负责监护人授权和隐私保护。Device Activity(设备活动)负责按时间窗口和使用阈值触发扩展代码。

import FamilyControls

Task {
    do {
        try await AuthorizationCenter.shared.requestAuthorization(for: .child)
    } catch {
        // Handle the error here.
    }
}

关键点:

  • FamilyControls 提供 Screen Time API 的授权入口。
  • Task 用来调用异步授权方法。
  • AuthorizationCenter.shared 是授权中心的共享实例。
  • requestAuthorization(for: .child) 请求在儿童设备上使用 Family Controls。
  • 失败时进入 catch,App 需要处理授权失败。

授权后,App 可以安排设备活动监控。Device Activity 在时间窗口开始、结束或达到阈值时调用扩展。

let schedule = DeviceActivitySchedule(
    intervalStart: DateComponents(hour: 15, minute: 0),
    intervalEnd: DateComponents(hour: 18, minute: 0),
    repeats: true
)

let center = DeviceActivityCenter()
try center.startMonitoring(.daily, during: schedule)

关键点:

  • DeviceActivitySchedule 描述一个监控时间窗口。
  • intervalStart 是窗口开始时间。
  • intervalEnd 是窗口结束时间。
  • repeats: true 表示每天重复。
  • DeviceActivityCenter 负责启动和停止监控。
  • startMonitoring(_:during:) 注册一个命名监控任务。

用 StoreKit 2 简化内购和订阅

00:46

recap 提到 StoreKit 2 可以快速实现 in-app subscriptions(App 内订阅)。对应 session 说明,StoreKit 2 提供 Swift 原生 API,用来获取商品、发起购买、处理交易、判断权益和客户状态。

获取商品信息可以直接调用 Product.products(for:)

let products = try await Product.products(for: ["fuel.small", "car.sports", "nav.pro"])

// 按类型分组
let consumables = products.filter { $0.type == .consumable }
let nonConsumables = products.filter { $0.type == .nonConsumable }
let subscriptions = products.filter { $0.type == .autoRenewable }

关键点:

  • Product.products(for:) 根据产品 ID 拉取商品信息。
  • try await 表示这是会抛错的异步调用。
  • products.filter 按商品类型分组。
  • .consumable 是消耗型商品。
  • .nonConsumable 是非消耗型商品。
  • .autoRenewable 是自动续期订阅。

购买流程也变成一次异步调用。

let result = try await product.purchase()

switch result {
case .success(let verification):
    let transaction = try checkVerified(verification)
    await transaction.finish()
case .userCancelled:
    break
case .pending:
    break
@unknown default:
    break
}

关键点:

  • product.purchase() 发起购买。
  • .success 返回验证结果。
  • checkVerified 用来确认交易已经通过验证。
  • transaction.finish() 告诉 StoreKit 交易处理完成。
  • .userCancelled 表示用户取消购买。
  • .pending 表示交易仍在等待批准或付款。

用 Nearby Interaction 连接第三方 UWB 配件

00:50

recap 提到 Nearby Interaction 可以和第三方配件配合使用。对应 session 解释,Nearby Interaction 使用 U1 的 Ultra-Wideband(超宽带)能力,为附近设备提供距离和方向更新。

在两个 iPhone 之间,基本流程是创建 session、设置 delegate、创建配置、运行 session。

let session = NISession()
session.delegate = self

let configuration = NINearbyPeerConfiguration(peerToken: peerDiscoveryToken)
session.run(configuration)

关键点:

  • NISession 是 Nearby Interaction 的核心对象。
  • session.delegate = self 接收附近对象更新。
  • NINearbyPeerConfiguration 用于两个运行同一 App 的 iPhone。
  • peerDiscoveryToken 来自对端设备,需要通过自己的连接通道交换。
  • session.run(configuration) 开始空间交互。

运行后,delegate 会收到 NINearbyObject 更新。对象里包含距离,部分场景还包含方向。

func session(_ session: NISession, didUpdate nearbyObjects: [NINearbyObject]) {
    guard let nearbyObject = nearbyObjects.first else { return }

    let distance = nearbyObject.distance
    let direction = nearbyObject.direction
}

关键点:

  • session(_:didUpdate:) 在 Nearby Interaction 有新测量值时触发。
  • nearbyObjects.first 取出最近一次更新的对象。
  • distance 表示到附近对象的距离。
  • direction 表示方向信息,具体可用性取决于设备和场景。

核心启发

1. 做一个环境声音提醒器

  • 做什么:识别门铃、狗叫、婴儿哭声,并在 Apple Watch 或 iPhone 上提醒。
  • 为什么值得做:Sound Analysis 内置分类器能识别多类离散声音,减少训练模型的成本。
  • 怎么开始:先用 SNClassifySoundRequest(classifierIdentifier: .version1) 查看支持标签,再接入麦克风音频流,把高置信度结果转成通知。

2. 做一个视频同步答题 App

  • 做什么:电视播放课程视频,iPad 自动显示对应题目和答案输入框。
  • 为什么值得做:ShazamKit 自定义目录可以把音频位置映射到课程内容,不依赖同一网络或播放器接口。
  • 怎么开始:为视频生成 reference signature,保存题目 offset;运行时用 SHSession 匹配音频,再用匹配偏移查找当前题目。

3. 做一个家庭作业模式

  • 做什么:孩子完成学习 App 的使用时长后,自动解除游戏 App 屏蔽。
  • 为什么值得做:Screen Time API 可以在儿童设备上执行限制,并通过 Device Activity 扩展响应使用阈值。
  • 怎么开始:先申请 Family Controls capability;用 AuthorizationCenter 请求授权;用 DeviceActivityCenter 建立 schedule;在扩展里用 Managed Settings 更新屏蔽规则。

4. 做一个轻量订阅功能

  • 做什么:为内容 App 加一个月度订阅,订阅用户解锁高级栏目。
  • 为什么值得做:StoreKit 2 把商品查询、购买和权益判断放进 Swift async/await 流程。
  • 怎么开始:在 App Store Connect 配置订阅产品;用 Product.products(for:) 拉取商品;调用 product.purchase();用当前权益判断是否展示高级内容。

5. 做一个配件寻物体验

  • 做什么:让 iPhone 指向一个兼容 UWB 配件,并显示距离变化。
  • 为什么值得做:Nearby Interaction 在 iOS 15 支持兼容的第三方硬件,App 可以拿到空间感知更新。
  • 怎么开始:阅读 Nearby Interaction 配件规格;在 App 里创建 NISession;与配件交换所需配置数据;根据距离和方向更新界面。

关联 Session

评论

GitHub Issues · utterances