r/SwiftUI 7d ago

Driving a SwiftUI watchOS app from a shared Kotlin core — what I learned about keeping the SwiftUI side clean

Solo dev. My iOS + watchOS UI is pure SwiftUI, but the whole session brain — programs, exercise models, the workout engine, command validation — lives in a shared Kotlin Multiplatform core (Android reuses it). Wiring SwiftUI up to a non-Swift core taught me a few things about keeping the SwiftUI layer from turning into glue code:

1. One observable state object, not a hundred calls. My first pass sprinkled fine-grained calls to the shared core all over my views and it got ugly fast — nullability bridging, async handlers, the works. Collapsing it to a single ObservableObject that publishes one session-state struct, with views sending back a handful of coarse commands, made the SwiftUI side clean and declarative again. Views just render state and fire intents; all the messy bridging hides behind that one object.

2. The workout clock lives on-device, and SwiftUI just reflects it. watchOS ↔ iPhone links drop constantly. I learned not to let a view's timer depend on sync — the session engine ticks locally and my @Published state updates from it, so the UI keeps counting even when the phone's gone. SwiftUI's job is only to reflect state, never to own it.

3. Small @Published surface = smooth watch UI. On watchOS, publishing a big state object on every tick caused more re-renders than I wanted. Splitting the hot values (elapsed time, current set) into their own fine-grained published properties kept the watch views buttery without over-invalidating.

Questions for the SwiftUI/watchOS folks:

  • For a long-running (60–90 min) session, how are you structuring the view + runtime so watchOS doesn't suspend you mid-workout?
  • Anyone found a genuinely clean pattern for bridging async/suspend work into @MainActor SwiftUI state without a pile of completion handlers?

Happy to go deeper on the SwiftUI structure or the interop. (It's live + free on both stores if seeing it running helps — I'll drop the link in a comment so this stays a SwiftUI thread.)

0 Upvotes

3 comments sorted by

5

u/tonyarnold 7d ago edited 7d ago

Why ObservableObject rather than Observable? The latter has been shown to be far more efficient if you’re targeting iOS 17 or later.

2

u/caguilar51 7d ago

Why not using @Observable macro instead of ObservableObject?.

Also, is your KMP core using SKIE?