Skip to main content

widgets

WidgetKit: timeline providers, widget families, intent configuration, deep links. Use when implementing app features related to widgets.

Datos de origen

Repositorio
moasq/nanowave
Última actividad en el origen
20 de marzo de 2026 a las 18:33
Idioma detectado de SKILL.md
inglés
Estrellas
32
Forks
9

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
widgets
description
WidgetKit: timeline providers, widget families, intent configuration, deep links. Use when implementing app features related to widgets.
tags
swiftui, ios, extensions
platforms
ios
# Widgets WIDGETS (WidgetKit): FRAMEWORK: import WidgetKit SETUP: - Requires separate widget extension target (kind: "widget" in plan extensions array) - Use AppGroup for data sharing between app and widget (auto-configured in project.yml) - Widget code goes in Targets/{WidgetName}/ directory TIMELINE ENTRY (data for a single widget render): struct MyEntry: TimelineEntry { let date: Date let title: String let value: Double } TIMELINE PROVIDER (supplies entries to the system): struct MyProvider: TimelineProvider { func placeholder(in context: Context) -> MyEntry { MyEntry(date: .now, title: "Placeholder", value: 0) } func getSnapshot(in context: Context, completion: @escaping (MyEntry) -> Void) { completion(MyEntry(date: .now, title: "Snapshot", value: 42)) } func getTimeline(in context: Context, completion: @escaping (Timeline<MyEntry>) -> Void) { let entry = MyEntry(date: .now, title: "Current", value: 42) let timeline = Timeline(entries: [entry], policy: .after(.now.addingTimeInterval(3600))) completion(timeline) } } WIDGET VIEW: struct MyWidgetView: View { var entry: MyProvider.Entry @Environment(\.widgetFamily) var family var body: some View { switch family { case .systemSmall: VStack { Text(entry.title).font(.headline); Text("\(Int(entry.value))").font(.largeTitle) } case .systemMedium: HStack { VStack(alignment: .leading) { Text(entry.title); Text("\(Int(entry.value))").font(.title) }; Spacer() } default: Text(entry.title) } } } WIDGET DEFINITION: struct MyWidget: Widget { let kind: String = "MyWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: MyProvider()) { entry in MyWidgetView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) // REQUIRED iOS 17+ } .configurationDisplayName("My Widget") .description("Shows current status") .supportedFamilies([.systemSmall, .systemMedium]) } } WIDGET BUNDLE (when multiple widgets exist): @main struct MyWidgetBundle: WidgetBundle { var body: some Widget { MyWidget() AnotherWidget() } } MANDATORY FILES (every widget extension MUST have ALL of these): 1. {Name}Bundle.swift — @main WidgetBundle entry point. Without this, the extension has no entry point → linker error "undefined symbol: _main" → CodeSign failure. 2. Provider.swift — TimelineProvider implementation. 3. WidgetView.swift — The widget's SwiftUI view. 4. (Optional) Intent.swift — AppIntent for interactive widgets (tap-to-complete, etc.). CRITICAL RULES: - .containerBackground(.fill.tertiary, for: .widget) is REQUIRED on the widget view in iOS 17+. Without it, the widget renders with no background. - The @main entry point is MANDATORY. Use @main on WidgetBundle (multiple widgets) or Widget (single widget). An extension target with NO @main will fail to link. - Shared data types between app and widget go in the Shared/ directory at the project root (both targets compile it). NEVER define shared types only in the main app's Models/ — the widget extension cannot see them. - Widget views must be self-contained — they cannot use @StateObject, @ObservedObject, or network calls. All data comes through the TimelineEntry. - Use .supportedFamilies() to declare which sizes the widget supports. - AppIntent static properties MUST use "static let" (not "static var") for Swift 6 concurrency safety. Using "static var" causes "not concurrency-safe because it is nonisolated global shared mutable state" error. --- ## Extension Configuration # Widgets WIDGETS (WidgetKit): FRAMEWORK: import WidgetKit SETUP: - Requires separate widget extension target (kind: "widget" in plan extensions array) - Use AppGroup for data sharing between app and widget (auto-configured in project.yml) - Widget code goes in Targets/{WidgetName}/ directory TIMELINE ENTRY (data for a single widget render): struct MyEntry: TimelineEntry { let date: Date let title: String let value: Double } TIMELINE PROVIDER (supplies entries to the system): struct MyProvider: TimelineProvider { func placeholder(in context: Context) -> MyEntry { MyEntry(date: .now, title: "Placeholder", value: 0) } func getSnapshot(in context: Context, completion: @escaping (MyEntry) -> Void) { completion(MyEntry(date: .now, title: "Snapshot", value: 42)) } func getTimeline(in context: Context, completion: @escaping (Timeline<MyEntry>) -> Void) { let entry = MyEntry(date: .now, title: "Current", value: 42) let timeline = Timeline(entries: [entry], policy: .after(.now.addingTimeInterval(3600))) completion(timeline) } } WIDGET VIEW: struct MyWidgetView: View { var entry: MyProvider.Entry @Environment(\.widgetFamily) var family var body: some View { switch family { case .systemSmall: VStack { Text(entry.title).font(AppTheme.Fonts.headline); Text("\(Int(entry.value))").font(AppTheme.Fonts.largeTitle) } case .systemMedium: HStack { VStack(alignment: .leading) { Text(entry.title); Text("\(Int(entry.value))").font(AppTheme.Fonts.title) }; Spacer() } default: Text(entry.title) } } } WIDGET DEFINITION: struct MyWidget: Widget { let kind: String = "MyWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: MyProvider()) { entry in MyWidgetView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) // REQUIRED iOS 17+ } .configurationDisplayName("My Widget") .description("Shows current status") .supportedFamilies([.systemSmall, .systemMedium]) } } WIDGET BUNDLE (when multiple widgets exist): @main struct MyWidgetBundle: WidgetBundle { var body: some Widget { MyWidget() AnotherWidget() } } MANDATORY FILES (every widget extension MUST have ALL of these): 1. {Name}Bundle.swift — @main WidgetBundle entry point. Without this, the extension has no entry point → linker error "undefined symbol: _main" → CodeSign failure. 2. Provider.swift — TimelineProvider implementation. 3. WidgetView.swift — The widget's SwiftUI view. 4. (Optional) Intent.swift — AppIntent for interactive widgets (tap-to-complete, etc.). CRITICAL RULES: - .containerBackground(.fill.tertiary, for: .widget) is REQUIRED on the widget view in iOS 17+. Without it, the widget renders with no background. - The @main entry point is MANDATORY. Use @main on WidgetBundle (multiple widgets) or Widget (single widget). An extension target with NO @main will fail to link. - Shared data types between app and widget go in the Shared/ directory at the project root (both targets compile it). NEVER define shared types only in the main app's Models/ — the widget extension cannot see them. - Widget views must be self-contained — they cannot use @StateObject, @ObservedObject, or network calls. All data comes through the TimelineEntry. - Use .supportedFamilies() to declare which sizes the widget supports. - AppIntent static properties MUST use "static let" (not "static var") for Swift 6 concurrency safety. Using "static var" causes "not concurrency-safe because it is nonisolated global shared mutable state" error.
Ver en GitHub