EasyApp

Control Center Widget

Learn how EasyAppSwiftUI integrates Control Center widgets

Overview

EasyAppWidgetControl targets iOS 18 Control Center widgets, enabling users to toggle an in-app timer from Control Center or the Lock Screen. This guide explains the control configuration, value provider, and how to extend the control with more actions.

Core Structure

  • Control entry EasyAppWidgetControl conforms to ControlWidget and registers through AppIntentControlConfiguration.
  • Value model The nested Value struct keeps track of the control state (isRunning, name).
  • Intent setup TimerConfiguration and StartTimerIntent let users edit the timer name and push state updates into the system.

Key implementation resides in EasyAppSwiftUI/EasyAppWidget/EasyAppWidgetControl.swift:

struct EasyAppWidgetControl: ControlWidget {
    static let kind: String = "sunshineLixun.EasyAppSwiftUI.EasyAppWidget"

    var body: some ControlWidgetConfiguration {
        AppIntentControlConfiguration(
            kind: Self.kind,
            provider: Provider()
        ) { value in
            ControlWidgetToggle(
                "Start Timer",
                isOn: value.isRunning,
                action: StartTimerIntent(value.name)
            ) { isRunning in
                Label(isRunning ? "On" : "Off", systemImage: "timer")
            }
        }
        .displayName("Timer")
        .description("A an example control that runs a timer.")
    }
}

Provider & State

  • Provider conforms to AppIntentControlValueProvider, returning the widget value for previews and live usage.
  • currentValue currently returns true to represent an active timer—replace this stub with real logic that checks application state.
struct Provider: AppIntentControlValueProvider {
    func previewValue(configuration: TimerConfiguration) -> Value {
        EasyAppWidgetControl.Value(isRunning: false, name: configuration.timerName)
    }

    func currentValue(configuration: TimerConfiguration) async throws -> Value {
        let isRunning = true // Check if the timer is running
        return EasyAppWidgetControl.Value(isRunning: isRunning, name: configuration.timerName)
    }
}

Intent Definitions

  • TimerConfiguration allows customization of the timer name within the Control Center editor.
  • StartTimerIntent implements SetValueIntent to perform the side effect of toggling the timer (currently a stub returning .result(); integrate your business logic here).
struct StartTimerIntent: SetValueIntent {
    static let title: LocalizedStringResource = "Start a timer"

    @Parameter(title: "Timer Name")
    var name: String

    @Parameter(title: "Timer is running")
    var value: Bool

    func perform() async throws -> some IntentResult {
        // Start the timer…
        return .result()
    }
}

Integration Steps

  1. Use Xcode 16 or later and enable the Control Center Widgets capability on the target.
  2. Include EasyAppWidgetControl() in EasyAppWidgetBundle.swift to register the control:
    @main
    struct EasyAppWidgetBundle: WidgetBundle {
        var body: some Widget {
            EasyAppWidget()
            EasyAppWidgetLiveActivity()
            EasyAppWidgetControl()
        }
    }
  3. Build and install the app on a device running iOS 18, then open the Control Center or Settings editor to add the Timer control.
  4. Tapping the control triggers StartTimerIntent, allowing you to execute timer logic in the intent handler.

Customization Tips

  • Adjust the ControlWidgetToggle strings or swap in ControlWidgetButton for other interaction patterns.
  • Extend the Value struct to surface additional state (e.g., remaining time) and return it from the provider.
  • Utilize LocalizedStringResource to localize the display name and description; update localization files accordingly.

Debugging & Known Issues

  • Control missing: ensure the widget extension targets iOS 18+ and test on physical hardware (Control Center widgets are not available in the simulator yet).
  • State not refreshing: verify the real data source and call the forthcoming Control Center APIs or WidgetCenter.shared.reloadAllTimelines() when appropriate.
  • Intent not firing: double-check StartTimerIntent.perform() and confirm App Intents entitlements are enabled in the main app.
  • Kind collisions: keep kind unique to avoid conflicts with other widgets in the bundle.

By integrating EasyAppWidgetControl, you can extend the EasyApp experience into the Control Center, complementing the Home Screen, Lock Screen, and Dynamic Island widgets.

Last updated on