1. 系統架構
HKTranApp 採用四層架構,各層職責分明:
| 層級 | 職責 | 關鍵元件 |
| 視圖層 | UI 渲染、用戶互動、導航 | DashboardView, SearchView, StopBrowserView |
| 服務層 | 業務邏輯、ETA 協調、搜尋 | ETARefreshEngine, RouteSearchEngine, NetworkMonitor |
| 轉接器層 | 各交通機構 API 通訊及數據標準化 | MTRAdapter, KMBAdapter, CitybusAdapter, FerryAdapter |
| 數據層 | 持久化儲存及記憶體模型 | SwiftData Entities, Canonical Models |
2. 技術棧
| 項目 | 技術 |
| 語言 | Swift 5.9+ |
| UI 框架 | SwiftUI |
| 持久化 | SwiftData |
| 網絡 | URLSession + NWPathMonitor |
| 並發 | Swift Concurrency(async/await, TaskGroup) |
| 第三方依賴 | 無(零第三方套件) |
3. 數據模型
3.1 SwiftData 持久化實體
| 實體 | 用途 | 關鍵欄位 |
SavedCombinationEntity | 已收藏的路線+車站組合 | operatorType, routeNumber, rawStopId, groupID, cachedETAMinutes |
WatchListGroup | 收藏清單分組 | name, sortOrder(最多 5 個) |
RouteIndexEntry | 本地路線搜尋索引 | routeNumber, originTC, destinationTC, searchableText, direction |
RouteIndexMeta | 索引下載狀態 | lastDownloadedAt, version, entryCount |
3.2 標準化記憶體模型
| 模型 | 用途 |
CanonicalRoute | 統一路線表示(跨交通工具) |
CanonicalStop | 統一車站表示 |
UnifiedETA | 統一 ETA 表示(分鐘、時間、月台、是否即時) |
RowETAState | 7 態 UI 狀態機 |
4. 轉接器層設計
每個交通機構的 API 格式各異,OperatorAdapter 協議將它們統一為標準模型。
4.1 各轉接器比較
| 項目 | MTR | KMB | Citybus | Ferry |
| API 來源 | rt.data.gov.hk | data.etabus.gov.hk | rt.data.gov.hk | 靜態時間表 |
| 速率限制 | 無 | 500ms | 無 | 不適用 |
| 方向值 | UP/DOWN | O/I | O/I | 靜態 |
| 月台資訊 | ✓ | ✗ | ✗ | ✗ |
| 即時數據 | ✓ | ✓ | ✓ | ✗(固定班次) |
4.2 KMB 速率限制器
使用 Swift Actor 實現的 KMBRateLimiter,確保每次請求間隔不少於 500ms。多個組合共享同一車站時,會合併為單一 API 呼叫。
5. 服務層設計
5.1 ETA 刷新引擎(ETARefreshEngine)
| 參數 | 值 |
| 自動刷新間隔 | 30 秒 |
| API 超時 | 10 秒 |
| MTR/CTB 並發 | 完全並行 |
| KMB 並發 | 按 stopId 批次(500ms 間隔) |
| 離線行為 | 跳過刷新,保留快取 |
5.2 網絡監控(NetworkMonitor)
- 基於
NWPathMonitor 的單例
- 狀態:上線(online)、離線(offline)、未知(unknown)
- 網絡恢復時自動觸發 ETA 刷新
5.3 路線搜尋引擎(RouteSearchEngine)
- 從 SwiftData 載入所有 RouteIndexEntry(~800-900 條)
- 記憶體內篩選:路線號碼 → 前綴 → 關鍵字
- 排序:交通工具優先級 → 精確匹配 → 前綴匹配
- 搜尋記錄:UserDefaults,最多 10 條
6. ETA 狀態機
每張路線卡片根據快取新鮮度及網絡狀態,顯示 7 種視覺狀態:
| 狀態 | 觸發條件 | 視覺效果 |
| 正常(Fresh) | 快取 < 90 秒 | 正常透明度 |
| 稍舊(Stale) | 90 秒 – 5 分鐘 | 80% 透明度,琥珀時鐘 |
| 離線(Offline) | 5–30 分鐘或無網絡 | 60% 透明度,離線圖示 |
| 錯誤(Error) | > 30 分鐘或 API 失敗 | 紅色三角形 |
| 暫停服務 | API 回傳空陣列 | 月亮圖示 +「暫停服務」 |
| 受限(Degraded) | ETA > 120 分鐘 | 「點擊查看站名」 |
| 載入中 | 首次載入,無快取 | 載入動畫 |
7. 離線與錯誤處理
- API 失敗不清除快取:用戶始終看到最後已知數據
- 逐個交通工具隔離:KMB 故障不影響 MTR/CTB 數據
- 離線模式:顯示快取資料 + 離線提示橫幅
- 網絡恢復:自動觸發即時刷新
- 首次啟動離線:顯示專用恢復頁面 + 重試按鈕
8. 安全性
| 項目 | 狀態 |
| API 認證 | 無需(全部使用公開開放數據 API) |
| 傳輸安全 | 僅使用 HTTPS |
| 個人資料收集 | 無 |
| 位置追蹤 | 無 |
| 廣告 / 追蹤 SDK | 無 |
| 數據儲存 | 全部儲存在裝置本地 |
| App Transport Security | 啟用(系統預設) |
9. 效能設計
| 指標 | 目標 |
| 12 個組合刷新時間 | < 2 秒 |
| 首個 ETA 回應 | < 200ms(漸進更新) |
| 路線索引大小 | ~463KB(~800-900 條記錄) |
| 搜尋結果上限 | 50 條 |
| 每卡片顯示 ETA | 最多 3 個 |
| 站點 ETA 預覽 | 首 10 個站 |
10. 資料來源
| 交通工具 | API | 認證 |
| 港鐵 MTR | rt.data.gov.hk(getSchedule.php) | 無 |
| 九巴 KMB | data.etabus.gov.hk | 無 |
| 城巴 Citybus | rt.data.gov.hk(CTB ETA API v2) | 無 |
| 渡輪 Ferry | 靜態時間表(運輸署公開資料) | 不適用 |
1. 系统架构
HKTranApp 采用四层架构,各层职责分明:
| 层级 | 职责 | 关键组件 |
| 视图层 | UI 渲染、用户交互、导航 | DashboardView, SearchView, StopBrowserView |
| 服务层 | 业务逻辑、ETA 协调、搜索 | ETARefreshEngine, RouteSearchEngine, NetworkMonitor |
| 转接器层 | 各交通机构 API 通讯及数据标准化 | MTRAdapter, KMBAdapter, CitybusAdapter, FerryAdapter |
| 数据层 | 持久化存储及内存模型 | SwiftData Entities, Canonical Models |
2. 技术栈
| 项目 | 技术 |
| 语言 | Swift 5.9+ |
| UI 框架 | SwiftUI |
| 持久化 | SwiftData |
| 网络 | URLSession + NWPathMonitor |
| 并发 | Swift Concurrency(async/await, TaskGroup) |
| 第三方依赖 | 无(零第三方包) |
3. 数据模型
3.1 SwiftData 持久化实体
| 实体 | 用途 | 关键字段 |
SavedCombinationEntity | 已收藏的路线+车站组合 | operatorType, routeNumber, rawStopId, groupID, cachedETAMinutes |
WatchListGroup | 收藏清单分组 | name, sortOrder(最多 5 个) |
RouteIndexEntry | 本地路线搜索索引 | routeNumber, originTC, destinationTC, searchableText, direction |
RouteIndexMeta | 索引下载状态 | lastDownloadedAt, version, entryCount |
3.2 标准化内存模型
| 模型 | 用途 |
CanonicalRoute | 统一路线表示(跨交通工具) |
CanonicalStop | 统一车站表示 |
UnifiedETA | 统一 ETA 表示(分钟、时间、月台、是否实时) |
RowETAState | 7 态 UI 状态机 |
4. 转接器层设计
每个交通机构的 API 格式各异,OperatorAdapter 协议将它们统一为标准模型。
4.1 各转接器比较
| 项目 | MTR | KMB | Citybus | Ferry |
| API 来源 | rt.data.gov.hk | data.etabus.gov.hk | rt.data.gov.hk | 静态时间表 |
| 速率限制 | 无 | 500ms | 无 | 不适用 |
| 方向值 | UP/DOWN | O/I | O/I | 静态 |
| 月台信息 | ✓ | ✗ | ✗ | ✗ |
| 实时数据 | ✓ | ✓ | ✓ | ✗(固定班次) |
4.2 KMB 速率限制器
使用 Swift Actor 实现的 KMBRateLimiter,确保每次请求间隔不少于 500ms。多个组合共享同一车站时,会合并为单一 API 调用。
5. 服务层设计
5.1 ETA 刷新引擎(ETARefreshEngine)
| 参数 | 值 |
| 自动刷新间隔 | 30 秒 |
| API 超时 | 10 秒 |
| MTR/CTB 并发 | 完全并行 |
| KMB 并发 | 按 stopId 批次(500ms 间隔) |
| 离线行为 | 跳过刷新,保留缓存 |
5.2 网络监控(NetworkMonitor)
- 基于
NWPathMonitor 的单例
- 状态:上线(online)、离线(offline)、未知(unknown)
- 网络恢复时自动触发 ETA 刷新
5.3 路线搜索引擎(RouteSearchEngine)
- 从 SwiftData 加载所有 RouteIndexEntry(~800-900 条)
- 内存内筛选:路线号码 → 前缀 → 关键字
- 排序:交通工具优先级 → 精确匹配 → 前缀匹配
- 搜索记录:UserDefaults,最多 10 条
6. ETA 状态机
| 状态 | 触发条件 | 视觉效果 |
| 正常(Fresh) | 缓存 < 90 秒 | 正常透明度 |
| 稍旧(Stale) | 90 秒 – 5 分钟 | 80% 透明度,琥珀时钟 |
| 离线(Offline) | 5–30 分钟或无网络 | 60% 透明度,离线图标 |
| 错误(Error) | > 30 分钟或 API 失败 | 红色三角形 |
| 暂停服务 | API 回传空数组 | 月亮图标 +「暂停服务」 |
| 受限(Degraded) | ETA > 120 分钟 | 「点击查看站名」 |
| 加载中 | 首次加载,无缓存 | 加载动画 |
7. 离线与错误处理
- API 失败不清除缓存:用户始终看到最后已知数据
- 逐个交通工具隔离:KMB 故障不影响 MTR/CTB 数据
- 离线模式:显示缓存资料 + 离线提示横幅
- 网络恢复:自动触发实时刷新
- 首次启动离线:显示专用恢复页面 + 重试按钮
8. 安全性
| 项目 | 状态 |
| API 认证 | 无需(全部使用公开开放数据 API) |
| 传输安全 | 仅使用 HTTPS |
| 个人资料收集 | 无 |
| 位置追踪 | 无 |
| 广告 / 追踪 SDK | 无 |
| 数据存储 | 全部存储在装置本地 |
| App Transport Security | 启用(系统预设) |
9. 性能设计
| 指标 | 目标 |
| 12 个组合刷新时间 | < 2 秒 |
| 首个 ETA 回应 | < 200ms(渐进更新) |
| 路线索引大小 | ~463KB(~800-900 条记录) |
| 搜索结果上限 | 50 条 |
| 每卡片显示 ETA | 最多 3 个 |
| 站点 ETA 预览 | 首 10 个站 |
10. 数据来源
| 交通工具 | API | 认证 |
| 港铁 MTR | rt.data.gov.hk(getSchedule.php) | 无 |
| 九巴 KMB | data.etabus.gov.hk | 无 |
| 城巴 Citybus | rt.data.gov.hk(CTB ETA API v2) | 无 |
| 渡轮 Ferry | 静态时间表(运输署公开资料) | 不适用 |
1. System Architecture
HKTranApp uses a four-layer architecture with clear separation of concerns:
| Layer | Responsibility | Key Components |
| View | UI rendering, user interaction, navigation | DashboardView, SearchView, StopBrowserView |
| Service | Business logic, ETA orchestration, search | ETARefreshEngine, RouteSearchEngine, NetworkMonitor |
| Adapter | Operator-specific API communication & normalization | MTRAdapter, KMBAdapter, CitybusAdapter, FerryAdapter |
| Data | Persistent storage & in-memory models | SwiftData Entities, Canonical Models |
2. Technology Stack
| Category | Technology |
| Language | Swift 5.9+ |
| UI Framework | SwiftUI |
| Persistence | SwiftData |
| Networking | URLSession + NWPathMonitor |
| Concurrency | Swift Concurrency (async/await, TaskGroup) |
| Third-Party Dependencies | None (zero third-party packages) |
3. Data Models
3.1 SwiftData Persistent Entities
| Entity | Purpose | Key Fields |
SavedCombinationEntity | Saved route+stop combination | operatorType, routeNumber, rawStopId, groupID, cachedETAMinutes |
WatchListGroup | Watch list group | name, sortOrder (max 5) |
RouteIndexEntry | Local route search index | routeNumber, originTC, destinationTC, searchableText, direction |
RouteIndexMeta | Index download state | lastDownloadedAt, version, entryCount |
3.2 Canonical In-Memory Models
| Model | Purpose |
CanonicalRoute | Unified route representation (cross-operator) |
CanonicalStop | Unified stop representation |
UnifiedETA | Unified ETA (minutes, time, platform, isLive) |
RowETAState | 7-state UI state machine |
4. Adapter Layer Design
Each transport operator has a different API format. The OperatorAdapter protocol normalizes them into canonical models.
4.1 Adapter Comparison
| Aspect | MTR | KMB | Citybus | Ferry |
| API Source | rt.data.gov.hk | data.etabus.gov.hk | rt.data.gov.hk | Static timetables |
| Rate Limit | None | 500ms | None | N/A |
| Direction Values | UP/DOWN | O/I | O/I | Static |
| Platform Info | ✓ | ✗ | ✗ | ✗ |
| Real-time Data | ✓ | ✓ | ✓ | ✗ (fixed schedule) |
4.2 KMB Rate Limiter
Implemented as a Swift Actor (KMBRateLimiter), ensuring ≥500ms between API requests. Multiple combinations sharing the same stop are batched into a single API call.
5. Service Layer Design
5.1 ETA Refresh Engine
| Parameter | Value |
| Auto-refresh interval | 30 seconds |
| API timeout | 10 seconds |
| MTR/CTB concurrency | Full parallel |
| KMB concurrency | Batched by stopId (500ms spacing) |
| Offline behavior | Skip refresh, preserve cache |
5.2 Network Monitor
- Singleton based on
NWPathMonitor
- States: online, offline, unknown
- Auto-triggers ETA refresh on network restore
5.3 Route Search Engine
- Loads all RouteIndexEntry records from SwiftData (~800-900 entries)
- In-memory filtering: route number → prefix → keyword
- Sorting: operator priority → exact match → prefix match
- Search history: UserDefaults, max 10 items
6. ETA State Machine
| State | Condition | Visual |
| Fresh | Cache < 90s | Full opacity |
| Stale | 90s – 5min | 80% opacity, amber clock |
| Offline | 5–30min or no network | 60% opacity, offline icon |
| Error | > 30min or API failure | Red triangle |
| No Service | API returns empty array | Moon icon + "No Service" |
| Degraded | ETA > 120min | "Tap to view stop name" |
| Loading | First load, no cache | Spinner |
7. Offline & Error Handling
- API failures never clear cache: Users always see last known data
- Per-operator isolation: KMB outage doesn't affect MTR/CTB data
- Offline mode: Cached data + offline banner indicator
- Network restore: Auto-triggers immediate refresh
- First launch offline: Dedicated recovery view + retry button
8. Security
| Aspect | Status |
| API Authentication | None required (all public open-data APIs) |
| Transport Security | HTTPS only |
| Personal Data Collection | None |
| Location Tracking | None |
| Ads / Tracking SDKs | None |
| Data Storage | All stored locally on device |
| App Transport Security | Enabled (system default) |
9. Performance
| Metric | Target |
| 12 combinations refresh time | < 2 seconds |
| First ETA response | < 200ms (incremental updates) |
| Route index size | ~463KB (~800-900 entries) |
| Search results limit | 50 entries |
| ETAs per card | Max 3 |
| Stop ETA preview | First 10 stops |
10. Data Sources
| Transport | API | Auth |
| MTR | rt.data.gov.hk (getSchedule.php) | None |
| KMB | data.etabus.gov.hk | None |
| Citybus | rt.data.gov.hk (CTB ETA API v2) | None |
| Ferry | Static timetables (Transport Department) | N/A |