Agent Wranglers

MOB-39Recommended

Give widgets and watch apps only the data they show

Share with widgets, watch apps and other extensions only the fields they display, and never a token. Clear that shared data at sign-out.

How to do it

Widgets and watch apps run apart from the main app, so data reaches them through a shared store. On iOS that is an App Group container, and copying the whole session there is a common mistake. Widgets also show on the lock screen and the watch face, where anyone holding the device can read them. Write a small snapshot with only the values on screen. Mark private values with privacySensitive() on iOS, so the lock screen hides them. If an extension must call your API, give it its own read-only token in the Keychain, never the main refresh token. Send watch data over the platform channel, such as WatchConnectivity or the Wear OS Data Layer. At sign-out, delete the snapshot and reload every widget, as part of wipe local data at sign-out. Tokens stay where keep tokens and keys in the Keychain or Keystore says.

Examples

Plain text example

struct WidgetSnapshot: Codable { let stepsToday: Int; let goal: Int }

func publishSnapshot(_ snapshot: WidgetSnapshot) throws {
    let shared = UserDefaults(suiteName: "group.com.example.fittrack")
    shared?.set(try JSONEncoder().encode(snapshot), forKey: "widget")
    WidgetCenter.shared.reloadAllTimelines()
}

func clearSnapshot() {
    UserDefaults(suiteName: "group.com.example.fittrack")?.removeObject(forKey: "widget")
    WidgetCenter.shared.reloadAllTimelines()
}

Kotlin example

data class WidgetSnapshot(val stepsToday: Int, val goal: Int)

suspend fun publishSnapshot(context: Context, snapshot: WidgetSnapshot) {
    context.getSharedPreferences("widget", Context.MODE_PRIVATE).edit()
        .putInt("stepsToday", snapshot.stepsToday).putInt("goal", snapshot.goal).apply()
    StepsWidget().updateAll(context)
}

suspend fun clearSnapshot(context: Context) {
    context.getSharedPreferences("widget", Context.MODE_PRIVATE).edit().clear().apply()
    StepsWidget().updateAll(context)
}

How to check

After sign-in on a test device, read the App Group container and the widget's stored state. They hold no token and no field outside the widget's list. After sign-out, every widget shows its signed-out view within 1 minute.

Sources

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/mobile-apps/data-on-the-device/widget-and-watch-data">Agent Wranglers mobile apps playbook</a>. In code you write, add a comment next to it with the same link.