צעדים, מרחק, קלוריות, דופק ושינה צריכים להימשך מפלטפורמת המשתמש (Android Health Connect באנדרואיד, Apple HealthKit ב-iOS) על ידי אפליקציית בריאות ותזונה — ולהציג אותם כפרופיל יומי נקי ומנוטרל כפילויות אחד. יש מעט מאוד קווי דמיון בין שני ה-APIs: מכניקות קריאה נפרדות, סכימות הרשאה שונות, ומבני נתונים שונים. בנינו את שכבת הסנכרון הנייד שגורמת להם להיראות כאחד.
האתגר
- שני APIs מקוריים שלא מסכימים על כלום. Apple HealthKit מחזיר אגרגטים יומיים באמצעות callbacks; Health Connect מחזיר דגימות גולמיות, מדורגות עם מטא-דאטה עשיר של מקורות. אותם מושגים, צורות שונות לחלוטין — והאפליקציה נאלצה לנרמל את שניהם לסכימה אחת.
- ספירה כפולה היא ברירת המחדל. טלפון, שעון חכם ואפליקציית צד שלישי יכולים כולם לדווח על אותם 8,000 צעדים. סיכום תמים שלהם מנפח כל מדד. האפליקציה נדרשה לזהות מקורות חופפים ולספור כל פעילות בעולם האמיתי פעם אחת.
- סוללה ורשת לא יכולים לשלם על טריות. נתוני בריאות משתנים כל היום, אך קריאת ההיסטוריה המלאה בכל סנכרון תרוקן את הסוללה ותרווה חיבורים סלולריים. קצב הסנכרון היה צריך להישאר קל תוך שמירה על תחושה חיה.
הרשאות יכולות לבלבל. עשרות סוגי נתונים, שני מודלי הרשאה, הגבלות קריאת רקע, ומשתמשים המעניקים הרשאות מסוימות אך לא אחרות — כל אלה שהאפליקציה צריכה לטפל בהם מבלי לקרוס את התהליך.
הפתרון שלנו
בנינו סנכרון תלת-שכבתי: שכבת קלט מקורית דקה קוראת את ה-API של כל פלטפורמה, שכבת נורמליזציה על המכשיר מנטרלת כפילויות ומגלגלת את הנתונים לסיכומים יומיים, והבקאנד שומר אותם ומתאים אותם ל-HealthLedger אמין אחד. הלקוח לעולם אינו שולח דגימות גולמיות — הוא שולח סיכומים יומיים נקיים ונכונים מבחינת אזור זמן.

ארכיטקטורה
- iOS קורא את Apple HealthKit דרך react-native-health; Android קורא את Health Connect דרך react-native-health-connect.
- נורמליזציה על המכשיר ממירה את הפלט של שתי הפלטפורמות לסכימה אחת ומנטרלת כפילויות לפני שכל דבר עוזב את הטלפון.
- טריגרים לסנכרון — 30 ימי היסטוריה בהפעלה ראשונה, ואז קריאות מצטברות קלות של יום אחד במרווחי זמן של 10 דקות ובכל חידוש אפליקציה.
- העברה — סיכומים יומיים מנורמלים נשלחים ב-POST לשרת הראשי ב- /user-health/system-activity, אשר מעביר אותם (עם user ID + timezone) למיקרו-שירות הבריאות.
- עמידות — כל מקור/יום נשמר באופן אידמפוטנטי כ- HealthSystemAggregate; זרם שינויים של MongoDB מתאים אותו אז ל-HealthLedger יומי.
Expo / React Native זרימת עבודה מנוהלת עם מודולי HealthKit ו-Health Connect מקוריים.
תכונות עיקריות
- חוזה נורמליזציה אחד לשני APIs. קוראים ספציפיים לפלטפורמה מזינים שלב אחד של formatHealthData אשר פולט את אותה צורה ללא קשר למקור — כך שהבקאנד וה-UI לעולם אינם מתפצלים על iOS מול Android.
2. נטרול כפילויות מבוסס מקסימום על המכשיר. בתוך כל מקור, קריאות של יום מסוכמות; ואז הערך היומי הוא ה- Math.max() לאורך מקורות (קלוריות מקושרות על ידי source|deviceType), כך שקריאות מרובות ממכשיר אחד נצברות, בעוד ששעון וטלפון המדווחים על אותו יום לא יוצרים כפילויות:
// Sum readings within each source, then take the max ACROSS sources dateSourceMap[date][source] += entry.count; const totalSteps = Math.ceil(Math.max(...Object.values(dateSourceMap[date]))); |
3.אסטרטגיית קריאה נכונה לכל פלטפורמה. קריאות Health Connect מדורגות עם לולאת pageToken (1,000 רשומות/עמוד); האגרגטים היומיים של HealthKit עוברים בלולאה יום אחר יום. כל ייחוד מוכל בקורא משלו, בלתי נראה לשאר האפליקציה.
4. קצב סנכרון שמכבד את הסוללה. מילוי חוזר מלא של 30 יום פועל רק פעם אחת (בפיקוח של דגל קריאה ראשונה); לאחר מכן, כל סנכרון מושך רק יום אחד — מהיר בסלולר, זול בצריכת חשמל.
5.קיבוץ יומי נכון מבחינת אזור זמן. דגימות מקובצות לתוך YYYY-MM-DD מפתחות באזור הזמן של המשתמש, כך שאימון בשעה 23:00 נוחת ביום הנכון ללא קשר למקום שבו השרת נמצא.
6.כתיבות בקאנד אידמפוטנטיות. כל סיכום מבצע upsert על (userId, deviceId, source, date) — שליחה חוזרת של אותו יום היא פעולה ללא שינוי (no-op), מה שהופך ניסיונות חוזרים וסנכרונים חופפים לבטוחים.
7.התמודדות גלויה עם כשלים. הרשאות חסרות, קריאות ריקות ושגיאות הגבלת קצב של Health Connect נתפסות ומוצגות בצורה נקייה — יום ריק לעולם אינו מפיל את הסנכרון, והמשתמש מקבל הנחיה ברורה במקום כשל שקט.
תוצאות
- פרופיל בריאות יחיד ועקבי לרוחב iOS ו-Android — ההבדל בפלטפורמה בלתי נראה לשאר האפליקציה.
- מקורות מכשיר/אפליקציה חופפים מנוטרלים כפילויות, כך שצעדים וקלוריות משקפים מציאות במקום סכומים מנופחים.
- סנכרונים מצטברים קלים שומרים על טריות הנתונים מבלי לרוקן את הסוללה או לשרוף נתונים סלולריים.
כתיבות אידמפוטנטיות ונכונות מבחינת אזור זמן משמעותן שכל מדד נוחת ביום הנכון, וניסיונות חוזרים לעולם אינם משחיתים את הרשומה.
מחסנית טכנולוגית
React Native (Expo) · react-native-health (HealthKit) · react-native-health-connect · NestJS · MongoDB · MongoDB Change Streams · moment-timezone

