Add Apple Watch complications for device monitoring, quick recording, and ask questions#4824
Add Apple Watch complications for device monitoring, quick recording, and ask questions#4824barrettj wants to merge 39 commits intoBasedHardware:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces Apple Watch complications for device monitoring, quick recording, and asking questions. The implementation uses WidgetKit and establishes a data pipeline between the Flutter app, iOS host, and the watchOS targets. The overall approach is solid, but I've identified a few significant issues. A hardcoded SDK path in the project file will break the build for other developers. The 'Start Device Recording' complication action is not implemented correctly and triggers the wrong action. Additionally, there's duplicated code for shared state management between the watch app and its complication, which poses a maintainability risk. Addressing these points will improve the robustness and correctness of this new feature.
| 4B5B6E291EA70190D1771F2D /* Pods-Runner.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release-dev.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release-dev.xcconfig"; sourceTree = "<group>"; }; | ||
| 51B2C0C6DC8817A51E8E80AF /* omiWatchAppComplication.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = omiWatchAppComplication.appex; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 556416C8748DD3DD2C06A9DB /* devProfile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = devProfile.xcconfig; path = Flutter/devProfile.xcconfig; sourceTree = "<group>"; }; | ||
| 5EC363DC52C4728F4BC45476 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS11.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; |
There was a problem hiding this comment.
The project file contains a hardcoded path to WatchOS11.0.sdk. This path is specific to your local development environment and will cause the build to fail for other developers who may have different Xcode or SDK versions installed. Project settings should use environment variables like SDKROOT or relative paths to ensure portability.
| if autoStartDeviceRecording { | ||
| viewModel.session.sendMessage(["method": "startRecording"], replyHandler: nil, errorHandler: nil) | ||
| } |
There was a problem hiding this comment.
The autoStartDeviceRecording feature is implemented by sending a startRecording message to the phone. However, on the iOS app side (AppDelegate.swift), the startRecording message is handled as a notification that the watch has started recording, not as a command to start recording on the Omi device. This means the "Start Device Recording" complication action will incorrectly start a watch recording instead.
To fix this, a new WCSession message should be introduced, for example, startDeviceRecording. The watch app should send this new message when autoStartDeviceRecording is true. The AppDelegate on the phone should then handle this new message and trigger the appropriate action to start recording on the Omi device.
| if autoStartDeviceRecording { | |
| viewModel.session.sendMessage(["method": "startRecording"], replyHandler: nil, errorHandler: nil) | |
| } | |
| if autoStartDeviceRecording { | |
| viewModel.session.sendMessage(["method": "startDeviceRecording"], replyHandler: nil, errorHandler: nil) | |
| } |
| // Created by Barrett Jacobsen | ||
|
|
||
| import Foundation | ||
| import WidgetKit | ||
|
|
||
| struct SharedState { | ||
| static let suiteName = "group.com.omi.watchapp" | ||
|
|
||
| private static var defaults: UserDefaults? { | ||
| UserDefaults(suiteName: suiteName) | ||
| } | ||
|
|
||
| // MARK: - Watch Recording State | ||
|
|
||
| static var isWatchRecording: Bool { | ||
| get { defaults?.bool(forKey: "isWatchRecording") ?? false } | ||
| set { | ||
| defaults?.set(newValue, forKey: "isWatchRecording") | ||
| defaults?.set(Date(), forKey: "lastUpdated") | ||
| WidgetCenter.shared.reloadAllTimelines() | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Device State (forwarded from iPhone via WatchConnectivity) | ||
|
|
||
| static var isDeviceRecording: Bool { | ||
| get { defaults?.bool(forKey: "isDeviceRecording") ?? false } | ||
| set { | ||
| defaults?.set(newValue, forKey: "isDeviceRecording") | ||
| defaults?.set(Date(), forKey: "lastUpdated") | ||
| WidgetCenter.shared.reloadAllTimelines() | ||
| } | ||
| } | ||
|
|
||
| static var isDeviceConnected: Bool { | ||
| get { defaults?.bool(forKey: "isDeviceConnected") ?? false } | ||
| set { | ||
| defaults?.set(newValue, forKey: "isDeviceConnected") | ||
| defaults?.set(Date(), forKey: "lastUpdated") | ||
| WidgetCenter.shared.reloadAllTimelines() | ||
| } | ||
| } | ||
|
|
||
| static var deviceBatteryLevel: Float { | ||
| get { defaults?.float(forKey: "deviceBatteryLevel") ?? -1 } | ||
| set { | ||
| defaults?.set(newValue, forKey: "deviceBatteryLevel") | ||
| defaults?.set(Date(), forKey: "lastUpdated") | ||
| WidgetCenter.shared.reloadAllTimelines() | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Ask Omi State | ||
|
|
||
| static var lastQuestionStatus: String { | ||
| get { defaults?.string(forKey: "lastQuestionStatus") ?? "idle" } | ||
| set { | ||
| defaults?.set(newValue, forKey: "lastQuestionStatus") | ||
| defaults?.set(Date(), forKey: "lastUpdated") | ||
| WidgetCenter.shared.reloadAllTimelines() | ||
| } | ||
| } | ||
|
|
||
| static var lastUpdated: Date { | ||
| defaults?.object(forKey: "lastUpdated") as? Date ?? .distantPast | ||
| } | ||
| } |
There was a problem hiding this comment.
The SharedState.swift file is duplicated in both the omiWatchApp and omiWatchAppComplication targets. This creates a maintainability issue, as any changes to the shared state logic must be manually synchronized between the two files. A discrepancy between them could lead to subtle bugs where the widget and the watch app behave differently.
This shared code should be moved into a shared framework that both the watch app and the widget extension can link against. This ensures there is a single source of truth for the shared state logic.
Review Fixes AppliedAddressed all review findings from Gemini and additional code review: Critical Fixes
Architecture Improvements
Minor Fixes
|
|
demo pls |
|
The only demo I can give is a (static) button on my watch face that launches the app (which is super not compelling) because it’s all going to a fake bundle id version of the app when all my data/device is in the real version - in order to use this, app group and provision profiles must be made on Apple’s servers for the associated bundle id - the joys of getting to make products for apples platform. Hence why I said I can’t really do any debugging or testing until it’s on TestFlight. This is a super straightforward watch complication though - and to be honest any watch complication that launches the app is better than the none that’s currently present (seriously - no one is going to press the dial and scroll though dozens of apps to find one when they want it - so if your apps aren’t complications for the watch then they aren’t used), so even if it doesn’t work it’d still actually provide a benefit for Apple Watch users. Sent from my iPhoneOn Feb 16, 2026, at 4:54 AM, Mohammed Mohsin ***@***.***> wrote:mdmohsin7 left a comment (BasedHardware/omi#4824)
demo pls
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: ***@***.***>
|
|
Hey @barrettj 👋 Thank you so much for taking the time to contribute to Omi! We truly appreciate you putting in the effort to submit this pull request. After careful review, we've decided not to merge this particular PR. Please don't take this personally — we genuinely try to merge as many contributions as possible, but sometimes we have to make tough calls based on:
Your contribution is still valuable to us, and we'd love to see you contribute again in the future! If you'd like feedback on how to improve this PR or want to discuss alternative approaches, please don't hesitate to reach out. Thank you for being part of the Omi community! 💜 |
|
Hey, friendly nudge — could you add a demo or end-to-end testing evidence to this PR? Screenshots, video, terminal output showing it working, anything that proves it runs correctly. In the AI era, generating code is the easy part — what really matters is showing it works. A solid demo or test run gives reviewers the confidence to merge quickly. Without it, PRs tend to sit in the queue longer than they need to. Thanks for contributing! |
|
Like I really can’t due to how iOS development works (who’ve I’ve been doing for 15 years - I’d love it if Apple wasn’t a pain). I’d have loved it if I didn’t have to take time out of my day to implement an obvious Apple Watch complication but it sort of seems like the entire project isn’t really all there/thought out (like when I ordered the device they didn’t include the extra charger I paid for) Seriously - someone else needs to implement this if you’re not going to merge it because there’s no point in having your Apple Watch app without a complication - it’s just not a thought out Apple Watch app as is, and I was trying to give it basic functionality. Sent from my iPhoneOn Feb 17, 2026, at 1:51 AM, Thinh ***@***.***> wrote:beastoin left a comment (BasedHardware/omi#4824)
Hey, friendly nudge — could you add a demo or end-to-end testing evidence to this PR? Screenshots, video, terminal output showing it working, anything that proves it runs correctly.
In the AI era, generating code is the easy part — what really matters is showing it works. A solid demo or test run gives reviewers the confidence to merge quickly. Without it, PRs tend to sit in the queue longer than they need to.
Thanks for contributing!
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
|
|
Like no offense, but if you guys don’t understand why I can’t demo it (app groups and bundle ids) - then you have zero business making an iOS app that handles user data, let alone always ok recordingsSent from my iPhoneOn Feb 17, 2026, at 1:51 AM, Thinh ***@***.***> wrote:beastoin left a comment (BasedHardware/omi#4824)
Hey, friendly nudge — could you add a demo or end-to-end testing evidence to this PR? Screenshots, video, terminal output showing it working, anything that proves it runs correctly.
In the AI era, generating code is the easy part — what really matters is showing it works. A solid demo or test run gives reviewers the confidence to merge quickly. Without it, PRs tend to sit in the queue longer than they need to.
Thanks for contributing!
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
|
|
Reopening — you were right. watchOS complications genuinely cannot be demoed without TestFlight provisioning profiles and proper app group/bundle ID configuration. Our demo policy didn't account for platform-specific constraints, and that's on us. We're reopening this and will review on the code merits. Sorry for the dismissal. |
|
Thanks for your patience, Barrett, and again — sincere apologies for the earlier closure. That was a mistake on our part, and we appreciate you sticking with us. We've now done a thorough code review of the full 2,956-line diff. Your watchOS architecture is well-structured — the complication family coverage ( That said, we found several items that need attention before merge. We're flagging these in the spirit of getting this to a solid, shippable state — not gatekeeping. Critical1. App Group entitlements not wired to watch targets The entitlements files exist ( 2. "Start Device Recording" not implemented end-to-end The watch sends a High3. Ask-question reply handler mismatch
4. Missing dependency
Medium5. Device state updates may be stale The 6. Unbounded audio accumulation
7. Xcode project version bump to 77 The Rebase8. Branch drift — 657 commits behind main Only 3 files overlap with recent main changes ( On demoing watchOS complicationsWe understand the complexity of watchOS provisioning — complication rendering on a real device does require proper entitlements and provisioning profiles that aren't trivial to set up for external reviewers. For what it's worth, Xcode's watchOS simulator paired with an iOS simulator can demo complication rendering and We're genuinely excited about this feature — watchOS complications would be a meaningful addition to Omi. We're committed to getting this merged and happy to help work through any of these items. Let us know if any of the findings above need clarification or if you see them differently. |
|
@barrettj Some pointers on the items mentioned above: Entitlements wiring: The Recording flow: Reply handler: Missing dep: Rebase: ~657 behind main, but only 3 files overlap so it should be manageable. Let us know if you have questions on any of these. |
Summary
/v2/voice-messages→local notification with answer)Setup Required
Before this works in production, the following Apple Developer portal setup is needed:
group.com.omi.watchappand add it to both the watch app and widget extension provisioning profilesomiWatchAppComplicationtarget with bundle ID$(APP_BUNDLE_IDENTIFIER).watchapp.complicationgroup.com.omi.watchappTesting Notes
I have not been able to fully test this due to the nature of iOS development requiring App Store Connect provisioning and team membership. I am on the TestFlight and am also the user who requested a complication feature on Reddit. Widget complications require a physical Apple Watch paired to a device with proper provisioning to fully validate.
The WidgetKit extension scheme (
omiWatchAppComplication) compiles with 0 errors against the existing Xcode project.Test plan
group.com.omi.watchappin Apple Developer portal