diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8f169a1..7f36529 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
**June 2026**
+Add a Workouts complication to your Apple Watch face — tap it to open the app straight from any watch face.
+
Prop your iPhone up during an Apple Watch workout and it now runs the same live flow side by side — Ready → work/rest → Finish with running timers — and you can drive from either device: swipe ahead, finish a set, or add one on whichever is closer, and the other follows along. Automatic moves, like a rest timer running out, advance both devices on their own.
Editing an exercise or split on iPhone now steps the Apple Watch out of that workout, showing it as "Editing on iPhone" until you're done — so the watch never keeps running an exercise whose plan you're changing.
diff --git a/README.md b/README.md
index c6aa4df..46f5212 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,9 @@ your own iCloud Drive.
bidirectional: drive from either device — swipe ahead, finish a set, add one — and the
other follows. Only *human* transitions are sent; automatic ones (a rest timer ending)
advance both devices independently off shared start times, so they never fight.
+- **Watch face complication** — a launcher complication you can place on any Apple
+ Watch face; tap it to open the app. Available in the circular, corner, inline, and
+ rectangular accessory slots.
- **iCloud Drive sync** — your data lives as human-readable JSON in your iCloud
Drive, synced across devices and visible in the Files app. iCloud is required.
diff --git a/Workouts Watch Widget/Resources/Info-WatchWidget.plist b/Workouts Watch Widget/Resources/Info-WatchWidget.plist
new file mode 100644
index 0000000..d50bcac
--- /dev/null
+++ b/Workouts Watch Widget/Resources/Info-WatchWidget.plist
@@ -0,0 +1,31 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ Workouts
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ $(MARKETING_VERSION)
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ ITSAppUsesNonExemptEncryption
+
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.widgetkit-extension
+
+
+
diff --git a/Workouts Watch Widget/WorkoutsWatchWidget.swift b/Workouts Watch Widget/WorkoutsWatchWidget.swift
new file mode 100644
index 0000000..001e987
--- /dev/null
+++ b/Workouts Watch Widget/WorkoutsWatchWidget.swift
@@ -0,0 +1,79 @@
+import SwiftUI
+import WidgetKit
+
+// A launcher complication: a static button on the watch face that opens the
+// Workouts app. It carries no data, so the timeline is a single entry that
+// never refreshes. Tapping any accessory widget launches its containing app,
+// so no deep link or App Group is needed.
+
+private struct LauncherEntry: TimelineEntry {
+ let date: Date
+}
+
+private struct LauncherProvider: TimelineProvider {
+ func placeholder(in context: Context) -> LauncherEntry {
+ LauncherEntry(date: .now)
+ }
+
+ func getSnapshot(in context: Context, completion: @escaping (LauncherEntry) -> Void) {
+ completion(LauncherEntry(date: .now))
+ }
+
+ func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {
+ // Nothing ever changes — one entry, never reload.
+ completion(Timeline(entries: [LauncherEntry(date: .now)], policy: .never))
+ }
+}
+
+private struct LauncherView: View {
+ @Environment(\.widgetFamily) private var family
+
+ private let glyph = "dumbbell.fill"
+
+ var body: some View {
+ switch family {
+ case .accessoryInline:
+ Label("Workouts", systemImage: glyph)
+ case .accessoryRectangular:
+ Label("Workouts", systemImage: glyph)
+ .font(.headline)
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
+ case .accessoryCorner:
+ Image(systemName: glyph)
+ .font(.title2)
+ .widgetLabel("Workouts")
+ default: // .accessoryCircular and any future families
+ ZStack {
+ AccessoryWidgetBackground()
+ Image(systemName: glyph)
+ .font(.title3)
+ }
+ }
+ }
+}
+
+struct WorkoutsLauncherComplication: Widget {
+ private let kind = "WorkoutsLauncher"
+
+ var body: some WidgetConfiguration {
+ StaticConfiguration(kind: kind, provider: LauncherProvider()) { _ in
+ LauncherView()
+ .containerBackground(.clear, for: .widget)
+ }
+ .configurationDisplayName("Open Workouts")
+ .description("Tap to open the Workouts app.")
+ .supportedFamilies([
+ .accessoryCircular,
+ .accessoryCorner,
+ .accessoryInline,
+ .accessoryRectangular,
+ ])
+ }
+}
+
+@main
+struct WorkoutsWatchWidgetBundle: WidgetBundle {
+ var body: some Widget {
+ WorkoutsLauncherComplication()
+ }
+}
diff --git a/project.yml b/project.yml
index 04ef7ff..199bbd7 100644
--- a/project.yml
+++ b/project.yml
@@ -78,6 +78,9 @@ targets:
excludes:
- "Resources/Info-*.plist"
- "Resources/*.entitlements"
+ dependencies:
+ - target: Workouts Watch Widget
+ embed: true
postBuildScripts:
- script: '"${SRCROOT}/Scripts/update_build_number.sh"'
name: Update Build Number
@@ -98,3 +101,20 @@ targets:
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
TARGETED_DEVICE_FAMILY: "4"
DEVELOPMENT_ASSET_PATHS: "\"Workouts Watch App/Preview Content\""
+
+ # ---- watchOS widget extension (a launcher complication for the watch face) --
+ Workouts Watch Widget:
+ type: app-extension
+ platform: watchOS
+ sources:
+ - path: Workouts Watch Widget
+ excludes:
+ - "Resources/Info-*.plist"
+ settings:
+ base:
+ PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Workouts.watchkitapp.widget
+ INFOPLIST_FILE: "Workouts Watch Widget/Resources/Info-WatchWidget.plist"
+ GENERATE_INFOPLIST_FILE: false
+ SWIFT_STRICT_CONCURRENCY: complete
+ WATCHOS_DEPLOYMENT_TARGET: "26.0"
+ TARGETED_DEVICE_FAMILY: "4"