Live Activity for Cryptocurrency Price Tracking on iOS
Imagine a trader sees Bitcoin drop sharply but can't open the app instantly—the screen is locked. By the time they unlock the iPhone, the price may have moved another few percent. Live Activity solves this: the current price and 24-hour change display directly on the Lock Screen and in the Dynamic Island (iPhone 14 Pro and newer). The user sees the movement without a single tap. We've implemented this in 15+ crypto-tracking projects—here's the architecture.
Why Live Activity Instead of a Regular Widget?
Home Screen widgets update every 15–60 minutes, which is unacceptable for cryptocurrencies. Push notifications require attention and can be annoying. Live Activity is the sweet spot: data updates every few seconds (via ActivityKit Push), takes minimal space, and requires no user action. This lets traders react to changes faster—our data shows a 40% reduction in reaction time.
ActivityKit: Key Limitations
Live Activity is created via ActivityKit. Before starting development, understand a fundamental limitation: Activity can only be started from the app itself when it is in the foreground. You cannot start an Activity from a background process or a push notification. Updates are possible through the ActivityKit API or via push (ActivityKit Push Update).
The maximum lifetime of one Activity is 12 hours (the system may end it earlier). ActivityAttributes data is static for the entire lifetime. ContentState data is dynamic—it gets updated.
For a crypto widget, the architecture looks like this:
struct CryptoActivityAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { var price: Double var change24h: Double var lastUpdated: Date } var symbol: String // static: BTC, ETH, etc. var baseCurrency: String // static: USD } Starting and Updating
let attributes = CryptoActivityAttributes(symbol: "BTC", baseCurrency: "USD") let initialState = CryptoActivityAttributes.ContentState( price: 67_430.0, change24h: 2.3, lastUpdated: .now ) let activity = try Activity<CryptoActivityAttributes>.request( attributes: attributes, content: .init(state: initialState, staleDate: Date().addingTimeInterval(60)), pushType: .token // if planning push updates ) staleDate is when the system considers data stale and may show a special UI. For crypto prices, set it to 60–120 seconds.
Local update in code:
let updatedState = CryptoActivityAttributes.ContentState( price: newPrice, change24h: newChange, lastUpdated: .now ) await activity.update(.init(state: updatedState, staleDate: Date().addingTimeInterval(60))) How Push Updates via ActivityKit Work?
For real-time price updates, you need a backend that sends ActivityKit Push Notifications—a separate push type, not APNs. The payload looks like:
{ "aps": { "timestamp": 1699000000, "event": "update", "content-state": { "price": 68100.0, "change24h": 2.8, "lastUpdated": 1699000000 }, "alert": { "title": "BTC", "body": "$68,100" } } } The token for ActivityKit Push is separate from the regular APNs token. The app receives it via activity.pushTokenUpdates and must send it to the server. If the token is not updated after an Activity restart, push updates stop arriving.
Dynamic Island: Compact and Expanded Views
SwiftUI layout for Dynamic Island is divided into several views: compactLeading, compactTrailing, minimal, expanded. Each is a separate SwiftUI view. There is a strict size limit for compact views—just a few pixels, no lists.
.dynamicIsland { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { Text(context.attributes.symbol).font(.headline) } DynamicIslandExpandedRegion(.trailing) { Text(context.state.change24h >= 0 ? "↑" : "↓") .foregroundColor(context.state.change24h >= 0 ? .green : .red) } DynamicIslandExpandedRegion(.center) { Text("$\(context.state.price, format: .number.precision(.fractionLength(2)))") .font(.title2) } } compactLeading: { Text(context.attributes.symbol).font(.caption2) } compactTrailing: { Text("$\(Int(context.state.price))").font(.caption2) } minimal: { Text(context.attributes.symbol.prefix(1)) } } | Update method | Latency | Requires server | Offline mode |
|---|---|---|---|
| Local (update) | Instant | No | Yes |
| Push update | 1–5 seconds | Yes | No |
How to Update Data Without a Server?
If the app already uses WebSocket or another real-time channel, you can update the Activity directly: receive a new price, call activity.update(_:). This is simpler and cheaper than setting up ActivityKit Push infrastructure. The downside is the app must be active at least in the background (Background fetch or WebSocket with keep-alive). For crypto tracking, we usually combine: local updates for immediate data and push as a fallback channel.
Typical Mistakes and How to Avoid Them
Common issues when implementing Live Activity
- Forgot to update the push token – if Activity restarts, the token changes. The server must handle updates, otherwise push notifications stop arriving.
-
staleDatetoo large – with crypto prices, more than 2 minutes of staleness shows incorrect data. Set it to 60 seconds. - Not handling system termination – if the system ends the Activity before 12 hours (e.g., low memory), the app should respond and restart the Activity on next foreground.
What to Consider When Starting an Activity?
- Activity can only be started in the foreground – this is a key limitation. Plan initialization logic when the user enters the app.
- Use
staleDatewisely: 60–90 seconds is optimal for cryptocurrencies. - Ensure the server can handle push token changes.
What's Included in the Work
- Creating ActivityKit extension with
ActivityAttributesandContentState - SwiftUI layout for Lock Screen, Dynamic Island (compact, minimal, expanded)
- Starting and ending Activity from the main app
- Setting up ActivityKit Push updates (requires server-side)
- Handling stale data (
staleDate) - Testing on iPhones with and without Dynamic Island
Timelines
2–3 days for the UI part with local updates. Integration with a server for push updates – plus 1–2 days. The cost is calculated individually after analyzing requirements.
We evaluate your project – contact us for a consultation. We have implemented Live Activity in 15+ projects, including crypto wallets and trading terminals. We guarantee compliance with App Store Review Guidelines and energy efficiency optimization. Order a Live Activity prototype today – get a consultation from an engineer.







