RORK LABJP
ENGINE — Rork Max is powered by Claude Code and Claude Opus 4.6, generating native Swift apps directlyCORE ML — Rork Max reaches on-device Core ML inference alongside HealthKit, HomeKit, NFC, and App ClipsSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunM&A — Rork acquired the app builder Paperline and says it will stay acquisitive to bring in engineering talentMARKET — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026GROWTH — The no-code AI platform market is projected to grow from $4.9B in 2024 to $24.8B by 2029ENGINE — Rork Max is powered by Claude Code and Claude Opus 4.6, generating native Swift apps directlyCORE ML — Rork Max reaches on-device Core ML inference alongside HealthKit, HomeKit, NFC, and App ClipsSEED — Rork raised a $15M seed led by Left Lane Capital in April 2026, joined by Peak XV and a16z SpeedrunM&A — Rork acquired the app builder Paperline and says it will stay acquisitive to bring in engineering talentMARKET — Gartner expects 75% of new applications to be built with low-code or no-code tools by the end of 2026GROWTH — The no-code AI platform market is projected to grow from $4.9B in 2024 to $24.8B by 2029
Articles/Dev Tools
Dev Tools/2026-06-14Advanced

Updating Live Activities Remotely: Putting Live Lock Screen Info on a Rork App

A practical design for updating Live Activities remotely through APNs so the Lock Screen and Dynamic Island stay current even when your app is closed. Covers push-to-start vs update tokens, the content-state payload, stale-date and the update budget, and bridging from Expo, with working code and the issues I hit in production.

Live Activities6ActivityKit4APNs3Expo139Rork Max219iOS107

Premium Article

When I decided to show wallpaper download progress on the Lock Screen, the first thing that tripped me up was a single question: while the app is closed, who actually updates this display? A Live Activity keeps running when your app is in the background, but if the app itself tries to drive the updates, it runs straight into background execution limits.

The answer was to build a path that updates the Live Activity directly from the server over APNs. Get this design wrong and a stale number stays glued to the Lock Screen forever. The approach is the same whether you ship an Expo app generated by Rork or a native Swift app generated by Rork Max. Here is what I learned, in the order it caused trouble.

There are two update paths

A Live Activity can be updated in two ways: locally and remotely. Local updates call Activity.update(...) during the short window your app is in the foreground. They are immediate, but they stop the moment the app sleeps.

Remote updates push to APNs, and the system rewrites the display when it receives them. Because they arrive even when the app is closed, anything that "moves forward outside the app" — progress, scores, a courier's position — needs this path.

Running several apps solo as an indie developer, I settled on shaping the initial display locally for the first few seconds, then sending every continuing update remotely. Mixing the two halfway makes it impossible to know which value is authoritative.

Two tokens: push-to-start and update

The first thing to grasp about remote updates is that there are two kinds of token. One is the push-to-start token, used to start a Live Activity that does not yet exist from the server side. The other is the update token, used to update one specific Activity that is already running.

The push-to-start token is a single token for the whole app, and you subscribe to it at launch. The update token is issued per Activity and arrives asynchronously right after you start one. Confuse the order and you end up able to start an Activity but never deliver a single update afterward.

import ActivityKit
 
@available(iOS 17.2, *)
func registerPushToStart() {
    Task {
        for await data in Activity<DownloadAttributes>.pushToStartTokenUpdates {
            let token = data.map { String(format: "%02x", $0) }.joined()
            // One per app. Register "this user can accept a fresh start" with the server
            await sendToServer(kind: "start", token: token, activityId: nil)
        }
    }
}
 
@available(iOS 16.1, *)
func observeUpdateToken(for activity: Activity<DownloadAttributes>) {
    Task {
        for await tokenData in activity.pushTokenUpdates {
            let token = tokenData.map { String(format: "%02x", $0) }.joined()
            // Issued per Activity, and it expires. Overwrite on the server every time
            await sendToServer(kind: "update", token: token, activityId: activity.id)
        }
    }
}

The key detail is that pushTokenUpdates is not a one-shot fetch but a stream that emits more than once. Tokens rotate, so if you save the first one and call it done, updates quietly stop a few hours later. Keep the subscription alive and overwrite the server each time one arrives.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Follow which of the two tokens (push-to-start vs update) to fetch in what order, and where to send each to your server, in working Swift and TypeScript
Take home an APNs payload design with stale-date, dismissal-date and relevance-score, plus a sense of the numbers that keep you inside the update budget
Understand the three production traps (a frozen Lock Screen, a broken Dynamic Island, an expired token) and the exact way around each
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-07-04
Start a Live Activity Without Launching Your Rork Max App — Designing Around a push-to-start Token That Never Arrives
In a native Swift app generated by Rork Max, you want to start a Live Activity from your server without the user ever opening the app. But the push-to-start token is never observed and it fails silently. Here's the cause and an observation layer that reliably captures the token.
Dev Tools2026-06-26
Show Your Rork App's Progress on the Lock Screen and Dynamic Island
Add Live Activities and ActivityKit to a Swift app generated by Rork Max so a meditation timer or delivery status appears on the lock screen and Dynamic Island, with working code and the submission gotchas to watch for.
Dev Tools2026-06-15
Showing Live Progress in the Dynamic Island from a Rork Max Swift App
How to add an ActivityKit Live Activity to a Swift app generated by Rork Max so progress shows in the Dynamic Island, plus the update-related pitfalls I hit in production.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →