iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
佛心分享-SideProject30

30 天開發一款真正能每天使用的散步 App系列 第 23

Widget(1)——Widget 的資料從哪裡來?

  • 分享至 

  • xImage
  •  

完成散步統計後,我希望把散步資料放到手機桌面上。

但 RN 的資料不能直接被原生 Widget 使用,因此這篇先處理資料怎麼從 RN 傳到 Widget。

React Native 和 Widget 無法直接共享資料

React Native 的畫面由 JavaScript 執行,而 Widget 是原生元件:

  • iOS 使用 WidgetKit 與 SwiftUI
  • Android 使用 Jetpack Glance

因此 Widget 無法直接取得 RN 裡的資料。

原本可以讓 Widget 自己查 SQLite,但這會讓 iOS、Android 都需要重新處理資料查詢與統計邏輯。

所以我最後採用:

RN 負責整理資料,Widget 只負責顯示。

Widget Snapshot

我建立了一份專門給 Widget 使用的 WidgetSnapshot

它不是完整的散步紀錄,只保存 Widget 需要的統計資料:

type WidgetSnapshot = {
  version: 1;
  generatedAt: string;
  userId: string | null;

  today: {
    distanceMeters: number;
    durationSeconds: number;
    walkCount: number;
    lastWalkCompletedAt: string | null;
  };

  week: {
    distanceMeters: number;
    activeDays: number;
    previousWeekDistanceMeters: number;
    distanceChangePercentage: number | null;
    dailyDistances: {
      date: string;
      distanceMeters: number;
    }[];
  };
};

RN 從 local_walk_history 取得正式散步紀錄,計算今日、本週、上週與最近七天的統計,再整理成 Snapshot。

Widget 收到後不需要查 SQLite,也不需要重新計算,直接使用 Snapshot 顯示。

Snapshot 也需要版本控制

Snapshot 加上 version,是為了處理 App 和 Widget 不一定會同時更新的情況。

例如未來新增:

  • 每月統計
  • 連續散步天數
  • 每日目標

Snapshot 格式就可能改變。

如果 Widget 還是舊版,就可能無法解析新的資料。

因此 Widget 讀取 Snapshot 時會先確認 version 是否支援,不支援就顯示空狀態,而不是因為格式不相容導致 Widget 出錯。

Snapshot 只保存正式統計

Widget 的資料來源只有 local_walk_history,也就是後端完成正式統計後保存的結果。

因此這些資料都不會直接進入 Widget:

  • 正在進行中的散步
  • 尚未同步完成的紀錄
  • 暫存在本地的 GPS

因為散步中的距離只是前端根據目前 GPS 計算出的暫時結果,正式距離仍然要等後端收到完整資料後重新計算。

例如 App 目前顯示 2.3 公里,後端最後計算的正式結果可能是 2.2 公里。

因此 Widget 只顯示正式統計,避免 App 和 Widget 出現不同的結果。

React Native 產生 Snapshot

RN 會先讀取 local_walk_history,再依照目前裝置的時區計算:

  • 今日統計
  • 本週統計
  • 上週統計
  • 最近七天距離

最後整理成 WidgetSnapshot

日期區間與統計邏輯都在 RN 完成,Widget 不需要重新實作。

Native Module

RN 產生 Snapshot 後,還需要把資料交給原生 Widget。

因此我把相關操作封裝成 Native Module,RN 只需要使用三個方法:

// 寫入最新的 Snapshot
updateWidgetSnapshot(snapshotJson);

// 通知 Widget 重新讀取資料
reloadWidgets();

// 清除 Widget 資料
clearWidgetSnapshot();

RN 不需要知道 iOS、Android 實際怎麼保存或讀取資料,平台差異全部由 Native Module 處理。

iOS 和 Android 如何共享資料?

兩個平台使用不同的共享方式:

  • iOS:App Group
    • Widget Extension 和主 App 是不同 Target,因此需要透過 App Group 建立共享空間。
  • Android:SharedPreferences
    • Widget 與主 App 可以共用同一份 SharedPreferences。

因此整體流程會變成:

local_walk_history
        ↓
  React Native
        ↓
  WidgetSnapshot
        ↓
  Native Module
        ↓
iOS App Group / Android SharedPreferences
        ↓
     Widget

RN 負責資料整理,Native Module 負責跨平台橋接,Widget 則只負責讀取與顯示。


上一篇
離線模式(3)——建立可恢復的同步機制
下一篇
Widget(2)——Widget 該顯示什麼資料
系列文
30 天開發一款真正能每天使用的散步 App25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言