← 所有文章

iOS 26 上的 HealthKit + SwiftUI:授權、樣本類型,以及兩款上架 App 的跨平台模式

HealthKit 是 Apple 諸多框架中,要在 SwiftUI App 裡正確上架最棘手的之一。授權流程暗藏一個瞬時失敗的陷阱,可能讓使用者永遠被鎖在外面。樣本類型彆扭地分裂為量化資料(HKQuantitySample,適用於飲水量、步數、卡路里)與類別資料(HKCategorySample,適用於正念冥想、睡眠、月經流量)。async API 介面則需要把 HealthKit 以回呼為基礎的呼叫包進withCheckedThrowingContinuation。到了 watchOS,模式又會再次轉變。

我已經在兩款上架 App 中導入了 HealthKit:Water(飲水量追蹤,約 192 行的HealthKitService1與 Return(正念冥想紀錄,約 171 行的HealthKitManager加上 155 行的HealthKitPermissionSheet)。2這兩款 App 共同涵蓋了 HealthKit 兩種主要樣本形態、兩個方向(讀取+寫入 vs. 僅寫入),以及單平台與跨平台的部署。

本文將逐一說明在生產環境存活下來的模式:權限預先說明的體驗、SwiftUI 的.healthDataAccessRequest修飾器與舊版requestAuthorization API 的對比、樣本查詢的 async 包裝層,以及 watchOS 特有的差異。

TL;DR

  • 授權狀態的回報是不對稱的。Apple 的 API 會可靠地告訴您「分享已授權」,但基於隱私考量並不會告訴您「讀取被拒絕」。本文涵蓋如何在不推測讀取狀態的前提下,偵測「使用者已被詢問但未授予」的情境。
  • 量化資料使用HKQuantitySample,搭配HKQuantityTypeHKUnit(Water 對飲水量採用.literUnit(with: .milli))。類別資料使用HKCategorySample,搭配HKCategoryType(Return 採用.mindfulSession)。
  • 權限預先說明的 sheet 是最常被略過的模式。Apple 的系統權限對話框冷冰冰;先顯示一個自訂的View來說明價值,能大幅提升授權通過率。
  • watchOS 的 HealthKit 需要獨立的HKHealthStore實例,重新詢問權限的規則更嚴格,而且無法用 SwiftUI 的 sheet 呈現權限預先說明。
  • Return 中的recordAuthorizationAttempt()模式可避免將瞬時的呈現失敗誤判為永久拒絕。

兩種樣本形態

HealthKit 沿著一條軸線切分樣本模型,這條軸線會影響您寫的每一行程式碼:3

樣本形態 典型用途 API
HKQuantitySample 飲水量、步數、卡路里、體重、心率 HKQuantityType + HKUnit + HKQuantity
HKCategorySample 正念冥想、睡眠、月經流量、性行為 HKCategoryType + HKCategoryValue
HKWorkout 結構化運動(跑步、游泳) HKWorkoutBuilderHKWorkoutSession

水量是量化資料。以下是HealthKitService.swift中的真實生產級程式碼:1

import HealthKit

@Observable
final class HealthKitService {
    static let shared = HealthKitService()

    private let healthStore = HKHealthStore()
    private let waterType = HKQuantityType(.dietaryWater)

    func logWater(amount: Double, date: Date = .now) async throws -> UUID {
        guard isAuthorized else { throw HealthKitError.notAuthorized }

        let quantity = HKQuantity(unit: .literUnit(with: .milli), doubleValue: amount)
        let sample = HKQuantitySample(
            type: waterType,
            quantity: quantity,
            start: date,
            end: date,
            metadata: [HKMetadataKeyWasUserEntered: true]
        )

        try await healthStore.save(sample)
        return sample.uuid
    }
}

來自生產環境的三個細節:

  1. .literUnit(with: .milli)是飲水量以毫升為單位的標準寫法。HealthKit 接受任何單位(美制液量盎司、公升),但建構子的選擇很重要,因為 Apple 內部會把所有資料正規化為公升。挑選.milli可讓您儲存整數值(240、500),而不是小數型公升(0.240、0.500)。
  2. HKMetadataKeyWasUserEntered: true會把樣本標記為手動輸入,而非量測得來。Health App 會在這類樣本上顯示一個小小的「手動輸入」指示;使用者對手動輸入資料的信任程度,與磅秤量測資料是不同的。
  3. 對於即時樣本,startend是同一個Date。量化資料是在[start, end]區間內累加,但「我剛才喝了 240 毫升」這種情境下,區間會收斂為一個時間點。

正念冥想紀錄是類別資料。以下是 Return 的HealthKitManager.swift中的真實生產級程式碼:2

import HealthKit

@MainActor
class HealthKitManager {
    static let shared = HealthKitManager()
    let healthStore = HKHealthStore()
    let mindfulType = HKCategoryType(.mindfulSession)

    func saveMindfulSession(start: Date, end: Date) async -> Bool {
        guard isAvailable else { return false }
        guard end > start else { return false }

        let sample = HKCategorySample(
            type: mindfulType,
            value: HKCategoryValue.notApplicable.rawValue,
            start: start,
            end: end
        )
        // ... save via healthStore.save(sample) ...
    }
}

兩個細節:

  1. HKCategoryValue.notApplicable.rawValue是承載結構的哨兵值。正念冥想紀錄沒有具有意義的「值」(它們是時長標記),因此 HealthKit 要求一個哨兵類別值(Int(0))來填入欄位以滿足型別系統。其他類別樣本則有更豐富的值:睡眠使用HKCategoryValueSleepAnalysis.asleep等等。
  2. 對類別樣本而言,start/end區間是有實際意義的。一場 10 分鐘的正念冥想就是startend相差 10 分鐘,而 HealthKit 每日的正念分鐘總和會把這些時長加總。

授權流程(以及它的陷阱)

HealthKit 的授權是刻意設計為不對稱的。authorizationStatus(for:)會誠實回報分享/寫入狀態(.sharingAuthorized.sharingDenied.notDetermined),但讀取的授予或拒絕無法直接透過該 API 觀察。Apple 刻意隱藏讀取狀態,以防止 App 推斷使用者的 Health 個人檔案中存有哪些資料。4您只能間接得知讀取的決定:若使用者授予讀取,查詢就會回傳資料;若使用者拒絕讀取,查詢就會回傳空結果。您的程式碼沒有「讀取被拒絕」這個訊號可以分支判斷。

兩款 App 各自以不同方式繞過這個限制。

Water 會讀取自己寫入的樣本。因為 Water 同時寫入也讀取飲水紀錄(用以填充「今日歷程」),它的授權流程必須處理「讀取被拒絕但無法觀察」的情境:1

private var typesToShare: Set<HKSampleType> { [waterType] }
private var typesToRead: Set<HKSampleType> { [waterType] }

func requestAuthorization() async throws {
    guard isHealthDataAvailable else { throw HealthKitError.notAvailable }

    try await healthStore.requestAuthorization(toShare: typesToShare, read: typesToRead)

    await MainActor.run { checkAuthorizationStatus() }
}

func checkAuthorizationStatus() {
    authorizationStatus = healthStore.authorizationStatus(for: waterType)
    isAuthorized = authorizationStatus == .sharingAuthorized
}

請注意checkAuthorizationStatus沒有做的事:詢問讀取授權狀態。Water 只檢查分享狀態。如果使用者授予了分享卻拒絕了讀取,Water 的 UI 會顯示「無紀錄」,因為讀取會回傳空結果(並非因為明確的錯誤)。Water 信任分享決定,並讓資料的缺席自我說明。如果使用者在意,他們可以從「設定」中修正。

Return 只寫入。Return 會記錄正念冥想,但從不讀回;它顯示的冥想清單來自自己的NSUbiquitousKeyValueStore,而非 HealthKit。因此 Return 的授權請求是僅寫入的:2

func requestAuthorization() async -> Bool {
    guard isAvailable else { return false }

    do {
        try await healthStore.requestAuthorization(
            toShare: [mindfulType],
            read: []  // We only need write access
        )
        hasRequestedHealthKit = authorizationStatus != .notDetermined
        return isAuthorizedToWrite()
    } catch {
        hasRequestedHealthKit = authorizationStatus != .notDetermined
        return false
    }
}

read: []這個空集合是刻意的。索取您並不需要的讀取權限,會擴張您必須在 App 審查中說明的權限範圍,還會讓使用者看到「Return 想要讀取您的正念冥想紀錄」時感到困惑——而這款 App 顯然不需要這項權限。

陷阱所在。兩款 App 都收斂到同一個防禦性模式:只有在狀態實際上已脫離.notDetermined時,才把授權嘗試標記為「已完成」。天真的寫法是:

hasRequestedHealthKit = true  // ❌ wrong: treats transient failures as denial

正確的模式來自 Return:2

hasRequestedHealthKit = authorizationStatus != .notDetermined  // ✓ checks real state

為何重要:SwiftUI 的.healthDataAccessRequest修飾器與底層的requestAuthorization API 在某些情況下(sheet 衝突、view 生命週期中斷、瞬時的 OS 狀態)可能無法成功呈現系統對話框。如果您在檢查實際狀態之前就把嘗試標記為「已完成」,使用者會被困在這個狀態:您的 App 認為他們拒絕了,但其實系統對話框從未出現過。他們沒有路可以回頭,除非他們去「設定 → 隱私權 → 健康」手動授予;但他們不會想到要這樣做,因為他們根本沒看過您的提示。Return 的recordAuthorizationAttempt()正是為了這個情境而存在。

權限預先說明的 sheet

Apple 的 HealthKit 權限對話框雖然正確,卻沒能成功推銷。它列出您的 App 想要分享或讀取的類型,附上開關。對話框沒有提供為什麼這個 App 需要這些資料的脈絡,沒有好處的說明,沒有 App 的品牌呈現。使用者會點選「不允許」,因為提示完全脫離脈絡。

Return 提供了一個HealthKitPermissionSheet,它會在系統對話框出現之前顯示。該 sheet 把 Return 的 App 圖示放在 Apple Health 圖示旁邊(符合 HIG:Apple Health 圖示不得被裁切或加陰影),5說明好處(「追蹤您的修習」/「將您的冥想紀錄存為 Apple Health 中的正念分鐘」),列出三項好處(「在 Apple Health 中查看您的修習」、「跨所有裝置同步」、「與其他健康類 App 協作」),並以單一向前推進的Continue按鈕作結,由它觸發系統請求。以下是HealthKitPermissionSheet.swift的實際結構:6

struct HealthKitPermissionSheet: View {
    var onEnableRequested: () -> Void
    var theme: Theme

    var body: some View {
        VStack(spacing: 0) {
            HStack(spacing: 16) {
                Image("ReturnAppIcon")
                    .resizable().scaledToFit().frame(width: 80, height: 80)
                    .clipShape(RoundedRectangle(cornerRadius: 18))

                Text("+").font(.title)

                Image("AppleHealthIcon")
                    .resizable().scaledToFit().frame(width: 80, height: 80)
                    // No clipShape; Apple HIG forbids altering the Health icon
            }

            // Title + subtitle + benefits list

            // (See discussion below for the production comment.)
            Button { onEnableRequested() } label: {
                Text("Continue")
            }
        }
        .interactiveDismissDisabled(true)
    }
}

interactiveDismissDisabled(true)加上不存在任何取消按鈕,是刻意的設計選擇。Apple 的 App 審查指南 5.1.1 要求 App 必須尊重使用者的隱私設定,並禁止操弄、欺騙或強迫取得同意。10生產程式碼在Continue按鈕上方的註解寫道:

Single forward action: the system HealthKit dialog owns the real yes/no choice. Apple Guideline 5.1.1(iv): the priming screen may not include any exit/dismiss path that bypasses the system permission request.

這個說法比指南字面上的內容更嚴格(指南談的是尊重同意、不強迫,但並未以這些字眼規範 priming 畫面的 UX)。這是 Return 的詮釋,並把它寫進原始碼裡。其用意:拒絕的使用者應該在 Apple 的畫面上拒絕,而不是在 Return 的畫面上。結果就是一張只有單一向前動作、沒有逃生口的 sheet。文案與好處列表必須要能讓使用者點下按鈕;這裡沒有「我再想想」這個分支。

流程如下:

  1. 使用者在 Return 的設定中點選「連接健康」。
  2. 權限預先說明的 sheet 出現。使用者閱讀說明。
  3. 使用者點選 Continue。當requestAuthorization執行、系統對話框出現時,sheet 仍保持在畫面上。
  4. 使用者在系統對話框中接受(或拒絕)。Return 呼叫recordAuthorizationAttempt()捕捉結果,然後 sheet 關閉。

為何值得做。Apple 並未公開過權限預先說明 sheet 帶來授權率提升的官方數據,但每一位我聊過、同時做過 A/B 測試又有耐心跑完的 iOS 開發者都回報了相同的方向:權限預先說明的 sheet 會大幅提高分享授權通過率。這個模式現在已普遍到連 Apple 自家的範本(照片、相機、定位)也愈來愈常包含自己的權限預先說明 UX。

watchOS 變體

watchOS 的 HealthKit 與 iOS 共用同一套 API 介面(HKHealthStore、樣本類型、授權),但有三個結構性差異:

  1. 每個 watch App 都有獨立的HKHealthStorewatch App 與配對的 iPhone App 各自擁有獨立的HKHealthStore實例。兩者都可寫入使用者的 HealthKit 資料庫,也都能讀取自己的樣本。這個 store 並不跨配對裝置共用。
  2. 不能用 SwiftUI sheet 做權限預先說明。watchOS 的 view 階層並不像 iOS 那樣支援 sheet。權限預先說明的 UX 必須做成全螢幕。
  3. 更嚴格的重新詢問規則。如果使用者先前曾拒絕,watchOS 的requestAuthorization呼叫對於重新呈現系統對話框會更為保守;您可能需要引導使用者到 iPhone 上的 Watch App 變更設定,因為手錶本身沒有「設定 → 隱私權 → 健康」這個 UI。

以下是WatchHealthKitManager.swift中的真實生產級程式碼:7

@MainActor
class WatchHealthKitManager {
    static let shared = WatchHealthKitManager()
    let healthStore = HKHealthStore()
    let mindfulType = HKCategoryType(.mindfulSession)

    private init() {}

    var isAvailable: Bool { HKHealthStore.isHealthDataAvailable() }

    /// Returns true if the system request completed (success or already-authorized).
    /// Caller checks isAuthorizedToWrite() separately for the actual share state.
    func requestAuthorization() async -> Bool {
        guard isAvailable else { return false }
        do {
            try await healthStore.requestAuthorization(toShare: [mindfulType], read: [])
            return true
        } catch {
            return false
        }
    }

    func isAuthorizedToWrite() -> Bool {
        guard isAvailable else { return false }
        return healthStore.authorizationStatus(for: mindfulType) == .sharingAuthorized
    }
    // ... saveMindfulSession identical to iOS variant ...
}

切分是刻意的:requestAuthorization回報系統呼叫是否成功,而isAuthorizedToWrite回報使用者選擇的結果。在手錶上把這兩者切開可讓程式碼更容易推理;在 iOS 上,等價的切分則展現在HealthKitManager裡的recordAuthorizationAttempt()isAuthorizedToWrite()這對方法上。

Watch 類別的程式碼大約只有 iOS 版的一半大小,因為它不需要:

  • hasRequestedHealthKit這個 UserDefaults 旗標(手錶 UX 需要支援的二次嘗試路徑較少)。
  • HealthKitPermissionSheet的相關程式碼(watchOS 沒有 sheet UI)。
  • recordAuthorizationAttempt()方法(手錶較窄的流程,瞬時失敗的邊界案例也較少)。

代價是 watchOS App 偶爾會碰到「拒絕後無計可施」的狀態:使用者拒絕了系統對話框,又沒意識到他們可以從 iPhone 上的 Watch App 修正。在這種情況下,Return 會在 App 內顯示一段小指示(「在 iPhone 上開啟 Watch App → 我的手錶 → 隱私權 → 健康」),而非嘗試重新詢問。

我會怎麼重寫

從在兩款上架 App 中導入 HealthKit 學到的三個教訓。

兩種 API 都管用;選擇取決於呈現的脈絡。SwiftUI 的.healthDataAccessRequest修飾器把舊版的 API 包裝成更具宣告式的形態,並在 iPadOS 上正確處理呈現脈絡。Return 採用此修飾器,並在HealthKitManager上以公開的mindfulShareTypes屬性曝露其分享類型,供 SwiftUI 父 view 串接。Water 直接使用舊版的healthStore.requestAuthorization,因為 Water 的授權流程是從非 View 的脈絡(一個@Observable服務)執行的。這個切分是個有用的模式:當請求源自 SwiftUI 生命週期事件(sheet 內的按鈕點選)時,優先使用修飾器;當請求源自服務時,退回到舊版 API。

權限預先說明的 sheet 在 iOS 上值得投入工程成本,watchOS 上則不然。權限預先說明的 sheet 大概要花四小時的工(sheet view、文案、佈景主題、整合)。在 iOS 上,授權通過率的提升大到讓這四小時成為理所當然的投資。在 watchOS 上,等價的全螢幕權限預先說明會更具侵擾性(它佔據整個錶面,而非以 sheet 形態呈現),使用者比較不可能在小螢幕上閱讀冗長的文案,而手錶 UX 流程中讓使用者主動要求需要 HealthKit 的功能的入口也較少。我在 Return 的 watchOS 版上沒做這個 sheet,至今並不後悔。

hasRequestedHealthKit與當前授權狀態分開追蹤。HealthKit 的 API 會告訴您當前的授權狀態,但不會告訴您是否曾經詢問過。這個區別之所以重要,是因為第二次點擊時的正確行為取決於它:第一次點擊應呼叫requestAuthorization;如果使用者先前已拒絕,第二次點擊應顯示「設定 → 隱私權 → 健康」的提示,而非再次呼叫 API(在已被拒絕的狀態下會無聲地什麼都不做)。hasRequestedHealthKit這個 UserDefaults 旗標,正是讓第二次點擊真正派上用場的關鍵。

何時不該使用 HealthKit

拒絕也是設計的一部分。

不要因為可以寫入就寫入 HealthKit。一款專注計時器 App 不需要寫入運動紀錄;一款筆記 App 不需要記錄正念。把 HealthKit 加進來只因為它是「免費整合」,會擴大您的隱私足跡、授權摩擦,以及 App 審查問卷的範圍,卻沒給使用者帶來實質價值。

能不讀取就不要讀取 HealthKit。讀取存取在 App 審查中更難自圓其說,也更難向使用者解釋。許多讀取 HealthKit 的 App 其實只要寫入即可,讓使用者在 Apple Health 中查看自己的資料;讀取流程會讓表面積加倍,UX 收益卻微不足道。

不要為了純粹跨裝置的資料而在 Mac 上使用 HealthKit。macOS 自 macOS 13 起支援 HealthKit,但大多數 Health 資料源自 iPhone 或 Apple Watch。如果您的 Mac App 需要相同資料,請在 iPhone 上寫入 HealthKit,讓 Apple 的跨裝置同步把它帶到 Mac 上。從 Mac 直接寫入 HealthKit 雖然有效,但實務上罕見。

沒測試「先授權後撤回」這條路徑就不要上架。使用者授予權限後,可能會從「設定 → 隱私權 → 健康」撤回。您的 App 必須優雅地處理撤回,通常是在使用者下次嘗試使用該功能時,顯示「設定深度連結」的指示。Water 與 Return 都實作了這條路徑;兩者第一次都沒做對。

對於在 iOS 26+ 上以 SwiftUI 上架 HealthKit 的 App,這代表什麼

三個重點。

  1. 在設計 UI 之前,先決定要僅寫入還是讀+寫。僅寫入是更小的表面、更快的 App 審查。讀+寫更具彈性,但會帶來「讀取被拒絕無法觀察」的不對稱性。
  2. 在 iOS 上加上權限預先說明的 sheet。授權通過率的提升是真實的。正確使用 Apple 的圖示(不裁切、不加陰影),說明好處,再呼叫系統 API。
  3. 把 watchOS 的 HealthKit 視為 iOS 模式的較小、較簡化版本。儀式更少、沒有 sheet、單一目的的授權。引導使用者到 iPhone 上的 Watch App 進行重新授權。

請將本文與我為同一系列 App 撰寫的其他文章一併參考:給 Apple Intelligence 的型別化App Intents;用於跨 LLM 代理的MCP 伺服器;視覺層的Liquid Glass 模式;觸及多裝置的多平台上架。HealthKit 是資料來源層,位於視覺層與整合介面之下。8

FAQ

我可以讓 iOS App 與其 watchOS 擴充功能共用授權嗎?

不行。iOS 的HKHealthStore與 watchOS 的HKHealthStore各自獨立。使用者在每個平台上分別透過獨立的系統對話框授權。各端的程式碼只能檢查自己的授權狀態;您無法從手錶讀取 iOS 的狀態,反之亦然。

如果使用者撤回寫入權限,我的樣本會怎樣?

既有樣本仍然保留在 HealthKit 中。使用者若願意,可從 Health App 手動刪除,但撤回您 App 的存取只會停止新的寫入。您的 App 將無法再儲存或修改樣本,但使用者的歷史資料會被保留下來。

SwiftUI 的.healthDataAccessRequest修飾器在生產環境可以放心使用嗎?

它在 iOS 17.4+ 上可運作,並在後續版本中持續精進。Return 採用此修飾器(在HealthKitManager上曝露mindfulShareTypes供 SwiftUI 取用)。Water 直接使用舊版的healthStore.requestAuthorization,因為 Water 的請求源自非 View 的@Observable服務。請依請求發起的位置來選擇:SwiftUI 生命週期事件 → 修飾器;服務或非 View 脈絡 → 舊版 API。

為何即使我從未要求過分享存取,HealthKit 的授權狀態也會顯示.sharingAuthorized

它不會。狀態是針對每個HKObjectType分別的。如果您檢查authorizationStatus(for: heartRateType)卻從未請求過心率,您會得到.notDetermined。狀態只有在針對該特定類型成功授權後,才會變成.sharingAuthorized

我需要為 HealthKit 提供隱私權清單嗎?

需要。任何接觸 HealthKit 的 App 都必須在PrivacyInfo.xcprivacy(隱私權清單)以及 App Store Connect 的隱私權問卷中宣告其使用。相關項目是Info.plist中的NSHealthShareUsageDescriptionNSHealthUpdateUsageDescription,加上隱私權清單中對應的宣告。9

參考資料


  1. 生產程式碼位於Water/Water/Services/HealthKitService.swift(192 行)。作者開發的Water是一款 SwiftUI 飲水追蹤 App,可在 iOS、iPadOS、macOS、watchOS 與 visionOS 上使用。採用HKQuantitySample,搭配HKQuantityType(.dietaryWater)HKMetadataKeyWasUserEntered旗標。 

  2. 生產程式碼位於Return/Return/HealthKitManager.swift(171 行)。作者開發的Return是一款 SwiftUI 冥想計時 App,可在 iOS、iPadOS、macOS、watchOS 與 tvOS 上使用。採用HKCategorySample,搭配HKCategoryType(.mindfulSession)HKCategoryValue.notApplicable.rawValue。 

  3. Apple Developer,“HealthKit framework”。該框架的兩種主要樣本類型是HKQuantitySample(用於可量測的量值,如飲水量、步數、卡路里)與HKCategorySample(用於非量化事件,如正念冥想、睡眠、月經流量)。HKWorkout則涵蓋結構化運動。 

  4. Apple Developer,“Authorizing access to health data”。Apple 刻意隱藏讀取拒絕狀態,以防止 App 推斷使用者的 Health 個人檔案中存有哪些資料。authorizationStatus(for:)方法只會誠實回傳分享存取的結果。 

  5. Apple Developer,“Apple Health icon usage”《Human Interface Guidelines》。Apple 的 Health 圖示不得修改、裁切、加陰影或重新著色;在宣傳與權限預先說明 UI 中需以 1:1 的忠實度重現。 

  6. 生產程式碼位於Return/Return/HealthKitPermissionSheet.swift(155 行)。觸發系統 HealthKit 對話框前顯示的權限預先說明View。將 Return 的 App 圖示與 Apple Health 圖示並陳,說明好處,並在使用者點擊時透過回呼閉包觸發授權。 

  7. 生產程式碼位於Return/ReturnWatch Watch App/WatchHealthKitManager.swift(86 行)。HealthKitManager的 watchOS 變體。獨立的HKHealthStore、無hasRequestedHealthKit旗標、無權限 sheet 相關程式碼。 

  8. 作者分析:HealthKit 是 iOS App 的資料來源層、App Intents 是系統 AI 介面、MCP 是跨 LLM 代理介面、Liquid Glass 是視覺介面。這四層共同組成跨所有五個 Apple 平台的單一上架產品。 

  9. Apple Developer,“Privacy manifest files”。HealthKit 的使用必須在PrivacyInfo.xcprivacy中宣告,並在Info.plist中加入NSHealthShareUsageDescriptionNSHealthUpdateUsageDescription鍵。 

  10. Apple Developer,“App Review Guideline 5.1.1”。App 必須尊重使用者的隱私設定,在收集個人資料前取得同意,且不得操弄、欺騙或強迫取得同意。本文中關於 priming 畫面退出路徑的具體措辭,反映的是 Return 的詮釋,而非指南字面上的內容。 

相關文章

Liquid Glass in SwiftUI: Three Patterns From Shipping Return on iOS 26

Apple's Liquid Glass is a one-line SwiftUI API. Three patterns from Return go beyond .glassEffect(): glass on text via C…

17 分鐘閱讀

Five Apple Platforms, Three Shared Files: How Return Actually Ships Cross-Platform SwiftUI

Return runs on iPhone, iPad, Mac, Apple Watch, and Apple TV. Three Swift files are shared across all five targets out of…

18 分鐘閱讀

The Cleanup Layer Is the Real AI Agent Market

Charlie Labs pivoted from building agents to cleaning up after them. The AI agent market is moving from generation to pr…

15 分鐘閱讀