Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Greptile SummaryAdds iOS lock screen widget showing Omi device battery level and mic mute state, with real-time updates via App Group UserDefaults and MethodChannel bridge between Flutter and Swift. Implementation:
Architecture:
Notes:
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Device as BLE Device
participant DP as DeviceProvider
participant CP as CaptureProvider
participant BWS as BatteryWidgetService
participant MC as MethodChannel
participant AD as AppDelegate (Swift)
participant UD as UserDefaults (App Group)
participant WK as WidgetKit
participant Widget as Lock Screen Widget
Note over Device,Widget: Battery Update Flow
Device->>DP: Battery level change (BLE notification)
DP->>BWS: updateBatteryInfo(name, level, type, connected)
BWS->>MC: invokeMethod('updateBatteryInfo')
MC->>AD: Handle method call
AD->>UD: Write battery data (NOT mute state)
AD->>WK: reloadTimelines(ofKind: "OmiBatteryWidget")
WK->>Widget: Refresh timeline
Widget->>UD: Read battery info
Widget-->>Widget: Display battery %
Note over Device,Widget: Mute State Update Flow
CP->>BWS: updateMuteState(true/false)
Note over CP: Called BEFORE BLE stream cancel
BWS->>MC: invokeMethod('updateMuteState')
MC->>AD: Handle method call
AD->>UD: Write ONLY mute state
AD->>WK: reloadAllTimelines()
AD->>WK: reloadAllTimelines() after 0.5s delay
WK->>Widget: Refresh timeline
Widget->>UD: Read mute state
Widget-->>Widget: Show mic/mic.slash icon (red if muted)
Note over Device,Widget: Disconnect Flow
Device->>DP: Device disconnected
DP->>BWS: updateBatteryInfo(isConnected: false)
BWS->>MC: invokeMethod('updateBatteryInfo')
MC->>AD: Handle method call
AD->>UD: Set isConnected = false
AD->>WK: reloadTimelines(ofKind: "OmiBatteryWidget")
WK->>Widget: Refresh timeline
Widget->>UD: Read connection state
Widget-->>Widget: Display "Connect device"
Last reviewed commit: ce0ee23 |
|
resolve conflicts pls @krushnarout |
resolved conflicts, please review @mdmohsin7 |
beastoin
left a comment
There was a problem hiding this comment.
Review by @ryo (community PR reviewer)
Solid lock screen widget implementation — the WidgetKit architecture is clean, App Group sharing is properly wired, and the demo screenshots show it working across all states.
What's good:
- Proper App Group (
group.com.friend-app-with-wearable.ios12) matching the existing app - Smart separation of
updateBatteryInfoandupdateMuteStatechannels to avoid race conditions BatteryWidgetService(Flutter) is a clean singleton with iOS-only guardCODE_SIGN_ENTITLEMENTSis properly set in build settings for the widget extension- Supports
accessoryRectangularandaccessoryInlinefamilies with good disconnected-state handling
Must fix
1. CLAUDE.md changes are out of scope
This PR removes safety guardrails from the project's AI instruction file:
- Removes "Never push or create PRs unless explicitly asked"
- Removes "Commit locally by default"
- Changes push behavior from "if instructed by user" to "when you finish implementing"
These are unrelated to the lock screen widget and should not be in this PR. Please revert all CLAUDE.md changes.
2. Resolve merge conflicts
@mdmohsin7 requested conflict resolution — please rebase on latest main.
Should fix
3. defaults?.synchronize() is deprecated
UserDefaults.synchronize() has been unnecessary since iOS 12. Apple docs: "this method is unnecessary and shouldn't be used." Safe to remove both calls.
4. Double reloadAllTimelines() in mute handler
The mute state handler calls reloadAllTimelines() then calls it again 500ms later. If the first call isn't reliable enough, there may be a timing issue worth investigating rather than working around with a delayed retry.
Happy to re-review once the CLAUDE.md changes are reverted and conflicts resolved.
- Revert CLAUDE.md to origin/main (widget PR should not modify AI guardrails) - Remove deprecated UserDefaults.synchronize() calls (unnecessary since iOS 12) - Replace double reloadAllTimelines() + 500ms retry with a single call Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
All three points have been addressed: 1/ CLAUDE.md changes reverted 2/ defaults?.synchronize() removed — both calls are gone. Agreed, unnecessary since iOS 12. 3/ Double reloadAllTimelines() removed — replaced the reloadAllTimelines() + 500ms retry with a single call. The delayed retry was a workaround masking a timing issue rather than fixing it, so removing it is the right call. Ready for re-review! |
|
This is how it appears when no device was connected, and also no change after I connect the device @krushnarout |
|
@mdmohsin7 I tried again and it’s working correctly can you check this? Claude suggested this: Root cause of the CFPrefs error: The warning Using kCFPreferencesAnyUser with a container is only allowed for System Containers appears when the App Group is not registered in the reviewer's provisioning profile. Even though the entitlements file is correct in the code, at install time the signed binary needs a provisioning profile that explicitly includes group.com.friend-app-with-wearable.ios12. Without it, the widget extension can't read from the shared container → shows blank. The CFPrefs error (kCFPreferencesAnyUser with a container) and blank widget are a provisioning issue, not a code bug. The code correctly uses UserDefaults(suiteName: "group.com.friend-app-with-wearable.ios12") and the entitlements file has the App Group set on both targets. This error appears when the provisioning profile on the test device doesn't include the group.com.friend-app-with-wearable.ios12 App Group. The signed binary's embedded profile must list it — Xcode Automatically Manage Signing won't add it unless your Apple Developer account has the App Group registered. To fix: Go to developer.apple.com → Identifiers → App Groups → verify group.com.friend-app-with-wearable.ios12 exists |
Summary
WidgetCenter.reloadAllTimelineson every battery or mute state changeaccessoryRectangular(lock screen bar) andaccessoryInline(lock screen inline) familiesDetails
BatteryWidget/) — Swift target with sharedUserDefaultsvia App Groupgroup.com.friend-app-with-wearable.ios12BatteryWidgetService) —MethodChannelsends battery info and mute state to Swift independently, so mute reloads are never overwritten by battery updatesDemo
demo:
when muted:
when device is disconnected:
🤖 Generated with Claude Code