OPass 已採納中央 Push Gateway v1 與 Firebase Cloud Messaging(FCM)topic 推播契約。本 issue 追蹤 CCIP-iOS 的實作範圍與驗收;未勾選的項目仍待完成並附上對應 revision 的驗證結果。架構採納、實作完成與目標環境驗收分別記錄;部署、雲端設定與真實派送須另行取得授權。
架構摘要
CCIP-Admin-Bueno -> OPass Push Gateway -> FCM topic -> CCIP-Android / CCIP-iOS
- Gateway 由 OPass 團隊維運;活動由驗證成功的 Gateway key 決定,呼叫端不能指定
EVENT_ID 或完整 topic。
- CCIP-Server 不取得 Gateway key,也不參與推播發送。
- 推播內容一定是公開資訊;不建立 device registry,也不逐一向裝置發送。
- App 只有在活動登入成功後才訂閱;每個已登入活動各保留一個角色/推播語系 topic,切換目前活動不會取消其他活動的訂閱。
實作依據
- 已採納的共同契約:ADR 0001、OpenAPI。
- 內容保存與部署約束:ADR 0002;Gateway 在中央 D1 保存公開推播內容與已知派送結果,活動 key 對應仍由中央 secret 管理。運算、內容保存及原生成效交付均不得要求綁定有效付款方式。
- 跨專案驗收與環境輸入:測試與發布驗收;中央設定及 CSV 交付:操作文件。
- 程式碼檢視基準:
11edb506834e7772fbb74607822df202b06ea2b9
若實作需要改變 topic、payload 或跨 repository 責任,應先更新 Gateway 契約,再調整本 issue。
現況與風險
OPassApp.swift 在 App 啟動時初始化 OneSignal,並立即要求通知權限。
EventStore.redeem 只有在身分驗證成功後才加入 <EVENT_ID><ROLE> tag,符合「登入成功後才訂閱」的產品規則。
- 登入新身分時,只有現有 tag 數量至少為 2 才全部移除;恰好有一個舊 tag 時會留下舊身分。
signOut 也只是把 tag value 設為空字串。改成每個 FCM role topic 各送一則後,殘留訂閱可能造成重複通知。
- iOS 已有登出功能;目前活動、token 與 role 會分別儲存在
NSUbiquitousKeyValueStore、可同步 Keychain 與 per-event UserDefaults。每個活動的 FCM topic 對應是單一裝置的狀態,不得跟著 iCloud 或 Keychain 同步到其他裝置。
- 切換活動時可以回到過去已登入的活動,因此切換目前活動不得取消先前活動的訂閱。
- 專案已連接與 Android 相同的 Firebase 專案
opass-8b7db,但 App target 尚未加入 Firebase Messaging product。既有 SDK 已提供 Analytics、App Check、Crashlytics 與 Performance。
- OneSignal 另有 Swift Package、Notification Service Extension target、App Group entitlement 與 Diagnostic 畫面。Apple alert delivery export 仍需最小 FCM service extension,不能因不需要 rich media 就刪除所有 extension。
EventStore.loadAttendee 也會更新角色,且失敗時可能回退快取;只有成功向活動服務驗證的結果才能建立新的已驗證身分或角色,不能把另一裝置的 token/快取當成本安裝登入成功。
1. Firebase Messaging 與 OneSignal 清理
2. APNs、FCM registration 與通知權限
3. Topic 推導與訂閱同步
至少在下列時機同步訂閱:
4. 前景顯示與通知點擊
5. Analytics 與驗證
統計蒐集的預設、詢問時機、撤回方式及與既有 Analytics 設定的關係暫緩定案,先完成不依賴此選擇的功能。Analytics/delivery export 的使用者選擇流程及依該政策進行的成效驗收,待產品決策定案後完成;不得從 SDK 或舊程式的預設推定使用者同意。這項前置決策不阻擋 Gateway、Admin、基本通知、訂閱與導頁的獨立開發;拒絕或撤回統計不得影響通知使用。
人工驗收:
不在本 issue 範圍
- registration、FID 或 APNs token 上傳 API。
- App 內的 Gateway key、service account credential 或發布功能。
.all topic、逐裝置發送、OneSignal 相容層、UnifiedPush 或 rich media extension 功能;最小 FCM delivery-metrics extension 明確屬於本 issue。
- 補送流程、App 內報表、表單或現場轉化追蹤。
- 為這次遷移重構登入資料、iCloud 同步或整套 navigation architecture。
參考資料
OPass 已採納中央 Push Gateway v1 與 Firebase Cloud Messaging(FCM)topic 推播契約。本 issue 追蹤 CCIP-iOS 的實作範圍與驗收;未勾選的項目仍待完成並附上對應 revision 的驗證結果。架構採納、實作完成與目標環境驗收分別記錄;部署、雲端設定與真實派送須另行取得授權。
架構摘要
EVENT_ID或完整 topic。實作依據
11edb506834e7772fbb74607822df202b06ea2b9若實作需要改變 topic、payload 或跨 repository 責任,應先更新 Gateway 契約,再調整本 issue。
現況與風險
OPassApp.swift在 App 啟動時初始化 OneSignal,並立即要求通知權限。EventStore.redeem只有在身分驗證成功後才加入<EVENT_ID><ROLE>tag,符合「登入成功後才訂閱」的產品規則。signOut也只是把 tag value 設為空字串。改成每個 FCM role topic 各送一則後,殘留訂閱可能造成重複通知。NSUbiquitousKeyValueStore、可同步 Keychain 與 per-eventUserDefaults。每個活動的 FCM topic 對應是單一裝置的狀態,不得跟著 iCloud 或 Keychain 同步到其他裝置。opass-8b7db,但 App target 尚未加入 Firebase Messaging product。既有 SDK 已提供 Analytics、App Check、Crashlytics 與 Performance。EventStore.loadAttendee也會更新角色,且失敗時可能回退快取;只有成功向活動服務驗證的結果才能建立新的已驗證身分或角色,不能把另一裝置的 token/快取當成本安裝登入成功。1. Firebase Messaging 與 OneSignal 清理
Package.resolved的12.17.0更新為包含 FID 相關修正的穩定相容版本:12.18.0修復既有 token cache 阻止 FID registration,12.19.0修復啟用 FID 後語系變更誤判 registration 失效。依官方 release notes 核對後續修正及工具鏈需求,提交實際解析的版本;不建立第二套 Firebase 設定。app.opass.ccip的 APNs authentication key 可供開發與正式環境使用。OneSignal-XCFrameworkpackage reference。remote-notificationbackground mode 與必要的 FCM callback,但 alert delivery export 必須在 service extension 執行,不用主 App 背景 callback 取代。2. APNs、FCM registration 與通知權限
AppDelegate同時擔任UNUserNotificationCenterDelegate與 Firebase Messaging delegate。Info.plist設定FirebaseMessagingInstallationIdEnabled = YES,採用 Firebase Messaging 目前的 FID registration 流程,不沿用已棄用的 registration token API。registerForRemoteNotifications(),並在 APNs registration callback 明確把 APNs token 交給 Firebase Messaging;SwiftUI App 不依賴隱含 swizzling 完成這一步。MessagingDelegate.messaging(_:didReceiveRegistration:)收到成功的 FID registration 時觸發 topic 同步,但不得把 registration、FID 或 APNs token 上傳到 Gateway 或寫入 log。3. Topic 推導與訂閱同步
EVENT_ID與ROLE均符合[A-Za-z0-9_-]{1,64},並拒絕角色all。zh及其延伸標籤、nan-Hant-*與nan-Latn-*使用正體中文zh-Hant,其他使用英文en;本 repo 的既有nan是漢字台語資源識別,在推播語系轉換時視同nan-Hant。UserDefaults。新安裝應在驗證登入後重新訂閱,不能只以「不同步」推定不會還原。redeem、loadAttendee與同步 token 驗證須綁定發起時的活動與登入身分版本;套用成功或失效結果前,確認身分未被登出、重新登入或新同步 token 取代。檢查與狀態更新須在同一主執行緒操作內完成;過期回應不得更新 attendee/role、清除新憑證或觸發訂閱,不能只序列化 SDK 操作。至少在下列時機同步訂閱:
EventStore.redeem驗證身分並儲存新 attendee、token 與 role 後。EventStore.loadAttendee成功驗證並更新角色後;快取 fallback 不建立新的已驗證登入,也不以暫時讀取失敗清除原訂閱。403直接當成 token 失效。EventStore.signOut清除登入資料時,只移除該活動的 topic。OPassStore.loadEvent後,核對所有已登入活動;切換目前活動本身不得取消其他活動的 topic。4. 前景顯示與通知點擊
UNUserNotificationCenterDelegate的前景 callback 顯示 list、banner 與預設提示音。push_id與event_id的 Gateway 通知。uri是 HTTPS 時交由系統開啟;沒有uri時,切換至event_id對應的活動,再沿用現有Router與FeatureDestinations.announcement進入公告頁。NotificationCenter或既有狀態傳遞通知點擊,不新增第二套 navigation framework。5. Analytics 與驗證
統計蒐集的預設、詢問時機、撤回方式及與既有 Analytics 設定的關係暫緩定案,先完成不依賴此選擇的功能。Analytics/delivery export 的使用者選擇流程及依該政策進行的成效驗收,待產品決策定案後完成;不得從 SDK 或舊程式的預設推定使用者同意。這項前置決策不阻擋 Gateway、Admin、基本通知、訂閱與導頁的獨立開發;拒絕或撤回統計不得影響通知使用。
apns.payload.aps.mutable-content為1;正常完成 content handler,失敗也不妨礙原通知顯示。不得把 token 或 FID 寫入 log。push_id確認 Apple 送達與背景通知開啟數可匯出;Sends不冒充送達,未取得的資料不補零,訊息/安裝實例不冒充自然人人數。CSV 由中央依 ADR 的保存與匯出流程交付,App 不新增報表或轉化追蹤。UNUserNotificationCenterDelegate的收到/點擊 callback 在允許統計時手動呼叫Messaging.messaging().appDidReceiveMessage(userInfo);不能以未停用 swizzling 推定 SwiftUI 已自動傳遞 Analytics。APNs token 仍依前述 callback 明確交給 Messaging;驗證前景與背景點擊不漏報、不重複計數,不新增自製統計事件。人工驗收:
loadAttendee/驗證回應晚於新身分登入,成功或失效都不得改寫新角色或 topic;登出後的舊回應不得恢復訂閱,登出再登入相同 token 也不得接受前一輪回應。zh-Hant與nanlocalization 使用zh-Hant推播;其他介面語言使用英文 fallback。uri、無uri的點擊結果都正確。uri的通知時,點擊後開啟event_id對應活動的公告頁。不在本 issue 範圍
.alltopic、逐裝置發送、OneSignal 相容層、UnifiedPush 或 rich media extension 功能;最小 FCM delivery-metrics extension 明確屬於本 issue。參考資料