סוחר רוצה לראות את שער הביטקוין במסך הראשי מבלי לפתוח את האפליקציה. אבל ווידג'ט קריפטו הוא לא רק תמונה יפה: הוא חייב לפעול לפי הכללים של מערכת ההפעלה הניידת. ב-iOS זה אומר WidgetKit + SwiftUI, וב-Android — Jetpack Glance או AppWidgetProvider קלאסי. שניהם עובדים על עיקרון הסנאפשוט: המערכת מבקשת את ממשק המשתמש הנוכחי ברגעים מסוימים, ומה שמוצג הוא באחריותנו. אנו מסתמכים על למעלה מ-30 פתרונות מיושמים לבורסות קריפטו ו-5+ שנות ניסיון בפיתוח מובייל. אופטימיזציה של תקציב לפרויקט שלך מתחילה בבחירת ארכיטקטורת הווידג'ט הנכונה.
למה ווידג'ט במסך הבית מסובך יותר ממה שזה נראה?
במבט ראשון, ווידג'ט נראה כמו רכיב ממשק משתמש פשוט. אבל המגבלות שלו בעדכון נתונים, במיוחד עבור מטבעות קריפטו, דורשות ארכיטקטורה מחושבת היטב. בואו נפרק את הבעיות המרכזיות והפתרונות שלהן — על סמך הניסיון שלנו.
איך WidgetKit מגביל עדכונים?
WidgetKit לא מאפשר לווידג'ט לבצע בקשות רשת בזמן אמת. הווידג'ט מקבל נתונים דרך TimelineProvider, שמחזיר מערך של TimelineEntry עם נתונים מוכנים מראש וחותמות זמן. המערכת עצמה מחליטה מתי לצייר מחדש את הווידג'ט.
עבור ווידג'ט קריפטו, אסטרטגיה טיפוסית היא עדכון כל 15–30 דקות באמצעות TimelineReloadPolicy.atEnd או .after(date:):
struct CryptoPriceEntry: TimelineEntry {
let date: Date
let symbol: String
let price: Decimal
let change24h: Double
}
struct CryptoPriceProvider: TimelineProvider {
func getTimeline(in context: Context, completion: @escaping (Timeline<CryptoPriceEntry>) -> Void) {
Task {
let price = try? await CryptoAPIClient.shared.fetchPrice(symbol: "BTC")
let entry = CryptoPriceEntry(date: .now, symbol: "BTC", price: price?.usd ?? 0, change24h: price?.change24h ?? 0)
let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: .now)!
let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
completion(timeline)
}
}
} מידע נוסף על מנגנון TimelineProvider
`TimelineProvider` הוא פרוטוקול שמגדיר שלוש שיטות: `placeholder`, `getSnapshot`, ו-`getTimeline`. `getTimeline` מחזיר מערך של רשומות, כל אחת מכילה תאריך ונתונים. המערכת משתמשת ברשומות אלה כדי להציג את הווידג'ט בנקודות הזמן המתאימות. לאחר הצגת הרשומה האחרונה, הווידג'ט מבקש ציר זמן חדש. מחזור זה חוסך במשאבים אך מגביל את תדירות העדכון.ניואנס חשוב: אפל מתאמת את תקציב העדכון. ווידג'טים עם תדירות עדכון גבוהה במכשירים עם סוללה חלשה מקבלים תקציב מופחת — עדכונים מתחילים להגיע בתדירות נמוכה יותר מהמבוקש. עבור אפליקציות מסחר שדורשות "נתונים לא ישנים מדקה", ווידג'ט אינו מתאים — אנו מסבירים זאת בכנות ללקוח לפני תחילת הפיתוח. אנו תמיד מנתחים דרישות עסקיות ומציעים חלופות, כמו התראות push או Live Activity. עמידה בהנחיות עוזרת להימנע מדחייה ב-App Store וחוסכת תקציב לעבודה חוזרת.
העברת נתונים בין האפליקציה הראשית לווידג'ט נעשית דרך struct CryptoPriceEntry: TimelineEntry { let date: Date let symbol: String let price: Decimal let change24h: Double } struct CryptoPriceProvider: TimelineProvider { func getTimeline(in context: Context, completion: @escaping (Timeline<CryptoPriceEntry>) -> Void) { Task { let price = try? await CryptoAPIClient.shared.fetchPrice(symbol: "BTC") let entry = CryptoPriceEntry(date: .now, symbol: "BTC", price: price?.usd ?? 0, change24h: price?.change24h ?? 0) let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: .now)! let timeline = Timeline(entries: [entry], policy: .after(nextUpdate)) completion(timeline) } } } + App Groups או UserDefaults(suiteName:) עם מיכל משותף. FileManager בתוך הווידג'ט עובד רק עם חבילת App Group — בלעדיה, הווידג'ט לא יראה נתונים שנכתבו על ידי האפליקציה הראשית.
גדלים והתאמת ממשק משתמש
WidgetKit תומך ב-4 גדלים: @AppStorage, .systemSmall, .systemMedium, .systemLarge (iPad בלבד). עבור ווידג'ט קריפטו, אנו בדרך כלל מיישמים קטן (סמל + מחיר + שינוי) ובינוני (מספר מטבעות בשורה). SwiftUI בווידג'טים לא תומך באנימציות, ScrollView, או אזורי לחיצה שרירותיים — רק .systemExtraLarge לקישורים עמוקים.
איך Android פותר את אותן משימות?
Jetpack Glance לעומת AppWidgetProvider קלאסי
| מאפיין | Jetpack Glance | AppWidgetProvider |
|---|---|---|
| API | בסגנון Compose | RemoteViews |
| תאריך הופעה | חדש יחסית | מההתחלה |
| מורכבות | נמוכה יותר (הצהרתי) | גבוהה יותר (אימפרטיבי) |
| מגבלות | לא כל ה-modifiers של Compose | שליטה מלאה |
Jetpack Glance הוא API בסגנון Compose לווידג'טים, שהופיע יחסית לאחרונה. הוא נוח יותר באופן ניכר מ-Link הקלאסי, אבל יש לו מגבלות: לא כל ה-modifiers של Compose נתמכים, וחלק מה-APIs עובדים אחרת מאשר ב-Compose הרגיל.
עדכוני נתונים דרך RemoteViews + GlanceAppWidgetManager.updateIf עם משימה תקופתית:
class CryptoPriceWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
val prefs = currentState<Preferences>()
val price = prefs[priceKey] ?: "—"
val change = prefs[changeKey] ?: "0.0"
provideContent {
Column(
modifier = GlanceModifier.fillMaxSize().background(Color.DarkGray).padding(12.dp)
) {
Text("BTC", style = TextStyle(color = ColorProvider(Color.White), fontSize = 12.sp))
Text(price, style = TextStyle(color = ColorProvider(Color.White), fontSize = 20.sp))
Text("$change%", style = TextStyle(
color = ColorProvider(if (change.startsWith("-")) Color.Red else Color.Green)
))
}
}
}
}מרווח העדכון המינימלי דרך WorkManager הוא 30 דקות (מגבלת Android). לעדכונים תכופים יותר, יש צורך ב-class CryptoPriceWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { val prefs = currentState<Preferences>() val price = prefs[priceKey] ?: "—" val change = prefs[changeKey] ?: "0.0" provideContent { Column( modifier = GlanceModifier.fillMaxSize().background(Color.DarkGray).padding(12.dp) ) { Text("BTC", style = TextStyle(color = ColorProvider(Color.White), fontSize = 12.sp)) Text(price, style = TextStyle(color = ColorProvider(Color.White), fontSize = 20.sp)) Text("$change%", style = TextStyle( color = ColorProvider(if (change.startsWith("-")) Color.Red else Color.Green) )) } } } } עם AppWidgetManager, אבל ב-Android 12+ משימות רקע מוסדרות על ידי Battery Optimizer — במצב Doze המרווחים מתארכים.
השוואה בין מנגנוני עדכון בין iOS ל-Android
| פרמטר | iOS WidgetKit | Android Jetpack Glance |
|---|---|---|
| מרווח מינימלי | 15-30 דקות (מוסדר על ידי המערכת) | 30 דקות (WorkManager יכול יותר) |
| מנגנון עדכון | TimelineProvider | GlanceAppWidget + WorkManager |
| מגבלות | תקציב סוללה ברמת מערכת ההפעלה | מצב Doze, Battery Optimizer |
| המלצה | לווידג'טים שלא דורשים זמן אמת | דומה |
איך להגדיר WidgetKit לווידג'ט קריפטו? (שלב אחר שלב)
- הוסף יעד Widget Extension ב-Xcode, כולל WidgetKit.
- צור מבנה
WorkManagerעם שדות נדרשים (מחיר, שינוי, תאריך). - יישם
PeriodicWorkRequest: שיטותTimelineEntry,TimelineProvider,placeholder. - ב-
getSnapshot, בצע בקשת API, צור רשומה, ציין את תאריך העדכון הבא. - צור תצוגת SwiftUI לווידג'ט באמצעות
getTimelineו-getTimeline. - הגדר App Groups לשיתוף נתונים עם האפליקציה הראשית.
- תמוך במספר גדלים דרך
Widget.
טעויות אופייניות בפיתוח ווידג'טים קריפטו
- התעלמות מתקציבי עדכון ב-iOS — הווידג'ט מפסיק לעדכן בסוללה חלשה.
- חוסר בממשק משתמש חלופי כשהרשת לא זמינה — המשתמש רואה ווידג'ט ריק.
- שימוש בחבילה שגויה עבור App Groups — נתונים לא מועברים.
- עדכונים תכופים מדי ב-Android — קונפליקט עם Battery Optimizer.
מה כלול בעבודה
- iOS: הרחבת WidgetKit,
StaticConfiguration, פריסת SwiftUI, App Groups לנתונים משותפים. - Android: ווידג'ט Jetpack Glance, WorkManager לעדכונים.
- אינטגרציה עם APIs של שערי חליפין (CoinGecko, Binance, CoinMarketCap, או backend משלך).
- תמיכה במספר גדלי ווידג'ט.
- קישור עמוק מהווידג'ט למסך האפליקציה הרצוי.
- בדיקת התנהגות ללא רשת ועם נתונים מיושנים.
- אחריות לתאימות עם App Store ו-Google Play (עמידה בהנחיות).
דוגמת תהליך עבודה (המקרה שלנו)
לפרויקט אחד — ארנק קריפטו עם תיק השקעות — יישמנו ווידג'ט iOS. בקשת הלקוח: עדכון כל 5 דקות. נאלצנו להשתמש בשילוב של WidgetKit + משימת רקע כדי לשמור על עדכניות. ב-Android — Glance + WorkManager עם מדיניות מרווח של 15 דקות. תוצאה: משתמשים חזרו לאפליקציה מהווידג'ט פי שניים יותר.
לוחות זמנים
3–5 ימים לכל פלטפורמה. אם צריך את שתיהן, 5–8 ימים בסך הכל, תוך התחשבות בלוגיקת שליפת הנתונים המשותפת. העלות מחושבת באופן אישי — צור קשר להערכת פרויקט תוך יום עסקים אחד. אם אתה צריך ווידג'ט לאפליקציית הקריפטו שלך, קבל ייעוץ מהצוות שלנו.







