ShelfWidgetProviding
Adopt to contribute one or more widgets to the notch shelf.
@MainActor protocol ShelfWidgetProviding : Droplet
Every member is main-actor isolated, inherited from Droplet.
When the host reads the descriptors
The host reads widgetDescriptors (and, for a ShelfWidgetDynamicLayoutProviding droplet, currentLayoutTraits(for:)):
- when the droplet starts, stops, is updated or is removed;
- after the droplet broadcasts a state change, which is any of these: its
objectWillChangefires (when the droplet is anObservableObject, as everydroppykit newscaffold is); one of its keys inDropletPreferencesServicechanges; an@Observableproperty read during the previous read changes (when the droplet is not anObservableObject); orstatePublisherfires; - when the droplet calls
invalidateLayout(for:), or itsshelfWidgetLayoutInvalidationPublisheremits.
Broadcasts are coalesced: a burst costs one read, and the host makes at most about four a second however often the droplet broadcasts. A read that changed nothing changes nothing on screen. One that did is applied live, without a relaunch: a new widget id appears in the shelf's widget picker, Settings and search; an id the droplet stops declaring leaves the shelf, while the user's placement of it is kept, so it returns to the same slot when the droplet offers it again; a changed title or symbol shows wherever the widget is listed; and a changed width or height re-lays the open shelf out on its own spring.
So a widget that should appear only once the droplet is configured can return no descriptor until then and declare it the moment it is: write the setting through host.preferences, or publish it on the droplet's own objectWillChange. State held by another object reaches the host only through one of those broadcasts: forward that object's objectWillChange to the droplet's own, or store the value in preferences. The widget's view is yours to keep current; a broadcast re-reads descriptors and does not rebuild a mounted view whose descriptor is unchanged.
Droppy 16.0.1 and earlier read the descriptors only when the droplet started, stopped, was updated or was removed, and when it called invalidateLayout(for:).
- Note: Maturity: Stable. Covered by the 1.0 compatibility promise. Host support: mounted. Droppy lays a droplet's widget out with the same algorithm as its own, from the traits in your descriptor. See Host support.
Properties
widgetDescriptors
@MainActor var widgetDescriptors: [ShelfWidgetDescriptor] { get }
The widgets this droplet provides right now. Read on the occasions listed above, never on a timer; keep it stable, cheap and free of side effects, and return the same descriptor for the same state, since the host compares each read with the last one to decide what to update.
Methods
makeWidgetSettingsPopover(_:)
@MainActor func makeWidgetSettingsPopover(_ id: ShelfWidgetID) -> AnyView?
Optional inline settings popover shown from the widget's gear affordance. Return nil when the widget has no inline settings. Defaults to nil.
makeWidgetView(_:context:)
@MainActor func makeWidgetView(_ id: ShelfWidgetID, context: ShelfWidgetContext) -> AnyView
Builds the widget's live view. Views render on dark shelf surfaces; use light foreground colors and the DroppyKit design tokens.
Hosts MUST call this with .solo, isPaired == false, and the settled solo slot width for a one-widget row. For every grouped row of two to four widgets, hosts MUST set isPaired and isCompact to true, pass each widget's settled slot width, and assign .leading, .middle, or .trailing according to its real position. Providers MUST render both the solo and grouped contexts from these facts; they must not infer row count from a product-specific width table.