iOS 26 上的 HealthKit + SwiftUI:授權、樣本類型,以及兩款上架 App 的跨平台模式
HealthKit 是 Apple 諸多框架中,要在 SwiftUI App 裡正確上架最棘手的之一。授權流程暗藏一個瞬時失敗的陷阱,可能讓使用者永遠被鎖在外面。樣本類型彆扭地分裂為量化資料(HKQuantitySample,適用於飲水量、步數、卡路里)與類別資料(HKCategorySample,適用於正念冥想、睡眠、月經流量)。async API 介面則需要把 HealthKit 以回呼為基礎的呼叫包進withCheckedThrowingContinuation。到了 watchOS,模式又會再次轉變。
我已經在兩款上架 App 中導入了 HealthKit:Water(飲水量追蹤,約 192 行的HealthKitService)1與 Return(正念冥想紀錄,約 171 行的HealthKitManager加上 155 行的HealthKitPermissionSheet)。2這兩款 App 共同涵蓋了 HealthKit 兩種主要樣本形態、兩個方向(讀取+寫入 vs. 僅寫入),以及單平台與跨平台的部署。
本文將逐一說明在生產環境存活下來的模式:權限預先說明的體驗、SwiftUI 的.healthDataAccessRequest修飾器與舊版requestAuthorization API 的對比、樣本查詢的 async 包裝層,以及 watchOS 特有的差異。
TL;DR
- 授權狀態的回報是不對稱的。Apple 的 API 會可靠地告訴您「分享已授權」,但基於隱私考量並不會告訴您「讀取被拒絕」。本文涵蓋如何在不推測讀取狀態的前提下,偵測「使用者已被詢問但未授予」的情境。
- 量化資料使用
HKQuantitySample,搭配HKQuantityType與HKUnit(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 |
結構化運動(跑步、游泳) | HKWorkoutBuilder、HKWorkoutSession |
水量是量化資料。以下是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
}
}
來自生產環境的三個細節:
.literUnit(with: .milli)是飲水量以毫升為單位的標準寫法。HealthKit 接受任何單位(美制液量盎司、公升),但建構子的選擇很重要,因為 Apple 內部會把所有資料正規化為公升。挑選.milli可讓您儲存整數值(240、500),而不是小數型公升(0.240、0.500)。HKMetadataKeyWasUserEntered: true會把樣本標記為手動輸入,而非量測得來。Health App 會在這類樣本上顯示一個小小的「手動輸入」指示;使用者對手動輸入資料的信任程度,與磅秤量測資料是不同的。- 對於即時樣本,
start與end是同一個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) ...
}
}
兩個細節:
HKCategoryValue.notApplicable.rawValue是承載結構的哨兵值。正念冥想紀錄沒有具有意義的「值」(它們是時長標記),因此 HealthKit 要求一個哨兵類別值(Int(0))來填入欄位以滿足型別系統。其他類別樣本則有更豐富的值:睡眠使用HKCategoryValueSleepAnalysis.asleep等等。- 對類別樣本而言,
start/end區間是有實際意義的。一場 10 分鐘的正念冥想就是start與end相差 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。文案與好處列表必須要能讓使用者點下按鈕;這裡沒有「我再想想」這個分支。
流程如下:
- 使用者在 Return 的設定中點選「連接健康」。
- 權限預先說明的 sheet 出現。使用者閱讀說明。
- 使用者點選 Continue。當
requestAuthorization執行、系統對話框出現時,sheet 仍保持在畫面上。 - 使用者在系統對話框中接受(或拒絕)。Return 呼叫
recordAuthorizationAttempt()捕捉結果,然後 sheet 關閉。
為何值得做。Apple 並未公開過權限預先說明 sheet 帶來授權率提升的官方數據,但每一位我聊過、同時做過 A/B 測試又有耐心跑完的 iOS 開發者都回報了相同的方向:權限預先說明的 sheet 會大幅提高分享授權通過率。這個模式現在已普遍到連 Apple 自家的範本(照片、相機、定位)也愈來愈常包含自己的權限預先說明 UX。
watchOS 變體
watchOS 的 HealthKit 與 iOS 共用同一套 API 介面(HKHealthStore、樣本類型、授權),但有三個結構性差異:
- 每個 watch App 都有獨立的
HKHealthStore。watch App 與配對的 iPhone App 各自擁有獨立的HKHealthStore實例。兩者都可寫入使用者的 HealthKit 資料庫,也都能讀取自己的樣本。這個 store 並不跨配對裝置共用。 - 不能用 SwiftUI sheet 做權限預先說明。watchOS 的 view 階層並不像 iOS 那樣支援 sheet。權限預先說明的 UX 必須做成全螢幕。
- 更嚴格的重新詢問規則。如果使用者先前曾拒絕,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,這代表什麼
三個重點。
- 在設計 UI 之前,先決定要僅寫入還是讀+寫。僅寫入是更小的表面、更快的 App 審查。讀+寫更具彈性,但會帶來「讀取被拒絕無法觀察」的不對稱性。
- 在 iOS 上加上權限預先說明的 sheet。授權通過率的提升是真實的。正確使用 Apple 的圖示(不裁切、不加陰影),說明好處,再呼叫系統 API。
- 把 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中的NSHealthShareUsageDescription與NSHealthUpdateUsageDescription,加上隱私權清單中對應的宣告。9
參考資料
-
生產程式碼位於
Water/Water/Services/HealthKitService.swift(192 行)。作者開發的Water是一款 SwiftUI 飲水追蹤 App,可在 iOS、iPadOS、macOS、watchOS 與 visionOS 上使用。採用HKQuantitySample,搭配HKQuantityType(.dietaryWater)與HKMetadataKeyWasUserEntered旗標。 ↩↩↩ -
生產程式碼位於
Return/Return/HealthKitManager.swift(171 行)。作者開發的Return是一款 SwiftUI 冥想計時 App,可在 iOS、iPadOS、macOS、watchOS 與 tvOS 上使用。採用HKCategorySample,搭配HKCategoryType(.mindfulSession)與HKCategoryValue.notApplicable.rawValue。 ↩↩↩↩ -
Apple Developer,“HealthKit framework”。該框架的兩種主要樣本類型是
HKQuantitySample(用於可量測的量值,如飲水量、步數、卡路里)與HKCategorySample(用於非量化事件,如正念冥想、睡眠、月經流量)。HKWorkout則涵蓋結構化運動。 ↩ -
Apple Developer,“Authorizing access to health data”。Apple 刻意隱藏讀取拒絕狀態,以防止 App 推斷使用者的 Health 個人檔案中存有哪些資料。
authorizationStatus(for:)方法只會誠實回傳分享存取的結果。 ↩ -
Apple Developer,“Apple Health icon usage”《Human Interface Guidelines》。Apple 的 Health 圖示不得修改、裁切、加陰影或重新著色;在宣傳與權限預先說明 UI 中需以 1:1 的忠實度重現。 ↩
-
生產程式碼位於
Return/Return/HealthKitPermissionSheet.swift(155 行)。觸發系統 HealthKit 對話框前顯示的權限預先說明View。將 Return 的 App 圖示與 Apple Health 圖示並陳,說明好處,並在使用者點擊時透過回呼閉包觸發授權。 ↩ -
生產程式碼位於
Return/ReturnWatch Watch App/WatchHealthKitManager.swift(86 行)。HealthKitManager的 watchOS 變體。獨立的HKHealthStore、無hasRequestedHealthKit旗標、無權限 sheet 相關程式碼。 ↩ -
作者分析:HealthKit 是 iOS App 的資料來源層、App Intents 是系統 AI 介面、MCP 是跨 LLM 代理介面、Liquid Glass 是視覺介面。這四層共同組成跨所有五個 Apple 平台的單一上架產品。 ↩
-
Apple Developer,“Privacy manifest files”。HealthKit 的使用必須在
PrivacyInfo.xcprivacy中宣告,並在Info.plist中加入NSHealthShareUsageDescription與NSHealthUpdateUsageDescription鍵。 ↩ -
Apple Developer,“App Review Guideline 5.1.1”。App 必須尊重使用者的隱私設定,在收集個人資料前取得同意,且不得操弄、欺騙或強迫取得同意。本文中關於 priming 畫面退出路徑的具體措辭,反映的是 Return 的詮釋,而非指南字面上的內容。 ↩