A trader wants to see the Bitcoin rate on the main screen without opening the app. But a cryptocurrency widget is not just a pretty picture: it must live by the rules of the mobile OS. On iOS that means WidgetKit + SwiftUI, on Android — Jetpack Glance or classic AppWidgetProvider. Both work on the snapshot principle: the system requests the current UI at certain moments, and what gets displayed is our responsibility. We rely on over 30 implemented solutions for crypto exchanges and 5+ years of mobile development experience. Budget optimization for your project starts with choosing the right widget architecture.
Why the Home Screen widget is trickier than it looks?
At first glance, a widget seems like a simple UI element. But its limitations in updating data, especially for cryptocurrencies, require a well-thought-out architecture. Let's break down the key problems and their solutions — based on our own experience.
How does WidgetKit limit updates?
WidgetKit does not allow the widget to make network requests in real time. The widget receives data via TimelineProvider, which returns an array of TimelineEntry with pre‑prepared data and timestamps. The system itself decides when to redraw the widget.
For a crypto widget, a typical strategy is to update every 15–30 minutes using TimelineReloadPolicy.atEnd or .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) } } } More about the TimelineProvider mechanism
`TimelineProvider` is a protocol that defines three methods: `placeholder`, `getSnapshot`, and `getTimeline`. `getTimeline` returns an array of entries, each containing a date and data. The system uses these entries to render the widget at the corresponding points in time. After the last entry is displayed, the widget requests a new timeline. This cycle saves resources but limits the update frequency.An important nuance: Apple adjusts the update budget. Widgets with high update frequency on low‑battery devices receive a reduced budget — updates start coming less frequently than requested. For trading apps requiring "data no older than 1 minute," a widget is not suitable — we honestly explain this to the client before development begins. We always analyze business requirements and offer alternatives, such as push notifications or Live Activity. Following the guidelines helps avoid App Store rejection and saves budget for rework.
Data transfer between the main app and the widget is done via App Groups + UserDefaults(suiteName:) or FileManager with a shared container. @AppStorage inside the widget works only with an App Group suite — without it, the widget won't see data written by the main app.
Sizes and UI adaptation
WidgetKit supports 4 sizes: .systemSmall, .systemMedium, .systemLarge, .systemExtraLarge (iPad only). For a crypto widget, we usually implement small (symbol + price + change) and medium (several coins in a row). SwiftUI in widgets does not support animations, ScrollView, or arbitrary tap areas — only Link for deep links.
How does Android solve the same tasks?
Jetpack Glance vs classic AppWidgetProvider
| Characteristic | Jetpack Glance | AppWidgetProvider |
|---|---|---|
| API | Compose‑like | RemoteViews |
| Date of appearance | Relatively recent | From the very beginning |
| Complexity | Lower (declarative) | Higher (imperative) |
| Limitations | Not all Compose modifiers | Full control |
Jetpack Glance is a Compose‑like API for widgets, appearing relatively recently. It is noticeably more convenient than classic RemoteViews, but has limitations: not all Compose modifiers are supported, and some APIs work differently than in regular Compose.
Data updates via GlanceAppWidgetManager.updateIf + WorkManager with a periodic task:
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) )) } } } } The minimum update interval via AppWidgetManager is 30 minutes (Android limitation). For more frequent updates, you need WorkManager with PeriodicWorkRequest, but on Android 12+ background tasks are regulated by Battery Optimizer — in Doze mode intervals stretch out.
Comparison of update mechanisms between iOS and Android
| Parameter | iOS WidgetKit | Android Jetpack Glance |
|---|---|---|
| Minimum interval | 15-30 minutes (system‑regulated) | 30 minutes (WorkManager can do more) |
| Update mechanism | TimelineProvider | GlanceAppWidget + WorkManager |
| Limitations | Battery budget at OS level | Doze mode, Battery Optimizer |
| Recommendation | For widgets not requiring real‑time | Similar |
How to set up WidgetKit for a crypto widget? (step‑by‑step)
- Add a Widget Extension target in Xcode, include WidgetKit.
- Create a
TimelineEntrystructure with required fields (price, change, date). - Implement
TimelineProvider: methodsplaceholder,getSnapshot,getTimeline. - In
getTimeline, make an API request, form an entry, specify the next update date. - Create a SwiftUI View for the widget using
WidgetandStaticConfiguration. - Configure App Groups to share data with the main app.
- Support multiple sizes via
supportedFamilies.
Typical mistakes when developing crypto widgets
- Ignoring update budgets on iOS — the widget stops updating at low battery.
- Missing fallback UI when the network is unavailable — the user sees an empty widget.
- Using the wrong suite for App Groups — data is not transferred.
- Too frequent updates on Android — conflict with Battery Optimizer.
What's included in the work
- iOS: WidgetKit extension,
TimelineProvider, SwiftUI layout, App Groups for shared data. - Android: Jetpack Glance widget, WorkManager for updates.
- Integration with exchange rate APIs (CoinGecko, Binance, CoinMarketCap, or your own backend).
- Support for multiple widget sizes.
- Deep link from the widget to the desired app screen.
- Testing of behavior without network and with stale data.
- Guarantee of compatibility with App Store and Google Play (following guidelines).
Example workflow (our case)
For one project — a crypto wallet with a portfolio — we implemented an iOS widget. Client request: update every 5 minutes. We had to use a combination of WidgetKit + background task to maintain recency. On Android — Glance + WorkManager with a 15‑minute interval policy. Result: users returned to the app from the widget twice as often.
Timelines
3–5 days per platform. If both are needed, 5–8 days total, considering the common data fetching logic. The cost is calculated individually — contact us for a project evaluation within 1 business day. If you need a widget for your cryptocurrency app, get a consultation from our team.







