知阅百微 见微知著

SwiftUI's userActivity and onContinueUserActivity modifiers enable powerful features like Handoff, Spotlight indexing, and Siri Shortcuts. But debugging these can be challenging—activities are system-managed, updates happen at unpredictable intervals, and failures often occur silently. There's a hidden debugging flag that can help.

The Debug Flag

Set _defaultSwiftUIActivityEnvironmentLoggingEnabled to true early in your app's lifecycle:

import SwiftUI

@main
struct MyApp: App {
    init() {
        #if DEBUG
        _defaultSwiftUIActivityEnvironmentLoggingEnabled = true
        #endif
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

What It Logs

Once enabled, SwiftUI logs detailed information about the userActivity lifecycle:


  1. Preference Changes
    : When userActivity modifiers are added, removed, or updated

  2. Handler Invocations
    : When your update closure is called

  3. Activity Updates
    : The actual NSUserActivity title and userInfo after updates

  4. Activation Conditions
    : Changes to handlesExternalEvents(preferring:allowing:) predicates

Example console output:


UserActivityPreferences changed: ...
Initializing advertised user activity: 
Invoking handler for ViewIdentity(...)
updated user activity "View Document" with userInfo ["documentID": "abc123"]

Common Issues the Logging Helps Debug


Activity not appearing in Handoff
: Check if the handler is being invoked and verify isEligibleForHandoff = true is set.


Spotlight search not working
: Verify isEligibleForSearch = true and that title is set. The logging shows exactly what's being advertised.


Wrong scene receiving activity
: The activation conditions logging shows the predicate matching logic. Verify your preferring and allowing sets are correct.


Updates not being saved
: The logging shows when needsSave triggers updates. If you don't see "updated user activity" messages, your handler may not be returning or the activity type doesn't match.

Requirements

  • SwiftUI Lifecycle (not UIKit AppDelegate lifecycle)
  • Activity types declared in Info.plist under NSUserActivityTypes
  • Same developer team for cross-device Handoff

Using the UIKit lifecycle with scene delegates will produce a warning:

Cannot use Scene methods for URL, NSUserActivity, and other External Events without using SwiftUI Lifecycle.

Implementation Details

The logging is implemented in SwiftUI's SceneBridge, which manages the connection between your views and the underlying scene infrastructure. The OpenSwiftUI implementation reveals how activities flow through the system:

  1. Views declare activity preferences via the userActivity modifier
  2. Preferences propagate up the view hierarchy and merge
  3. SceneBridge
    receives preference changes and manages NSUserActivity instances
  4. The activity's delegate (userActivityWillSave) triggers your update handlers
  5. Activities are attached to the root view controller (iOS) or window (macOS)

Further Reading

For comprehensive coverage of NSUserActivity features in SwiftUI, see Javier's excellent article: NSUserActivity with SwiftUI which covers Handoff, Spotlight Search, Universal Links, and Siri Shortcuts in depth.