Skip to content

feat(darwin): notify central of background connection events - #264

Merged
rohitsangwan01 merged 2 commits into
Navideck:mainfrom
jonasbark:ios-background-connect-notify-options
Jul 9, 2026
Merged

feat(darwin): notify central of background connection events#264
rohitsangwan01 merged 2 commits into
Navideck:mainfrom
jonasbark:ios-background-connect-notify-options

Conversation

@jonasbark

@jonasbark jonasbark commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Add an opt-in way to have CoreBluetooth surface connection / disconnection / characteristic-notification events that occur while the app is suspended, relaunching the app into the background to handle them.

connect() gains an optional platformConfig parameter carrying Apple-specific options (mirroring the existing PeripheralPlatformConfig pattern):

await bleDevice.connect(
  autoConnect: true,
  platformConfig: ConnectionPlatformConfig(
    apple: AppleConnectionOptions(
      notifyOnConnection: true,      // CBConnectPeripheralOptionNotifyOnConnectionKey
      notifyOnDisconnection: true,   // CBConnectPeripheralOptionNotifyOnDisconnectionKey
      notifyOnNotification: true,    // CBConnectPeripheralOptionNotifyOnNotificationKey
    ),
  ),
);

All flags default to false, so behavior is unchanged for existing consumers (these keys can show a system alert to the user while the app is suspended, and notifyOnNotification fires per characteristic notification — hence opt-in, per review feedback).

The new parameter is threaded through the pigeon connect signature (regenerated for Dart/Kotlin/Swift/C++) and all platform implementations; only iOS/macOS act on it, other platforms accept and ignore it. The Darwin change also consolidates the three previous manager.connect(...) call sites into a single connect(peripheral, options:); the existing iOS 17+ CBConnectPeripheralOptionEnableAutoReconnect is merged into the same dictionary, so that path is behaviorally unchanged.

Motivation

I maintain a fitness app (BikeControl) that stays connected to a smart trainer/sensors for the duration of a workout. Users routinely background the app (lock the phone, switch to music, etc.). Combined with the bluetooth-central background mode and the CoreBluetooth state restoration that already landed in 2.0/2.1, these options let the central keep reacting to peripheral events — most importantly reconnecting to a previously paired peripheral — while suspended, instead of going silent until the user foregrounds the app.

I've been carrying this as a one-line fork of universal_ble for a while. Everything else in that fork has since landed upstream — this is the only patch left, so I'd love to upstream it and retire the fork.

Testing

  • flutter analyze and flutter test pass (81/81).
  • Example app builds for macOS (Swift) and Android (Kotlin) with the regenerated pigeon files.
  • Behavior verified in production in BikeControl on iOS (as the previous always-on fork patch): backgrounded reconnection to a paired trainer wakes the app to handle the event.

🤖 Generated with Claude Code

Set CBConnectPeripheralOptionNotifyOnConnection/Disconnection/Notification
on connect() so CoreBluetooth surfaces connection, disconnection and
notification events that occur while the app is suspended (relaunching it
into the background to handle them). This keeps a backgrounded central —
e.g. a fitness app reconnecting to a previously paired trainer — responsive
without the user having to foreground the app.

Consolidated to a single manager.connect(options:) call; the iOS 17+
auto-reconnect option is merged into the same dictionary, so behavior on
that path is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request modifies the Swift connection logic in UniversalBlePlugin to unconditionally enable background connection options (CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey, and CBConnectPeripheralOptionNotifyOnNotificationKey) when connecting to a peripheral. Feedback indicates that enabling these options by default can trigger intrusive system alerts when the app is suspended, and recommends making them configurable or opt-in instead.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +222 to +226
var options: [String: Any] = [
CBConnectPeripheralOptionNotifyOnConnectionKey: true,
CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,
CBConnectPeripheralOptionNotifyOnNotificationKey: true,
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Enabling CBConnectPeripheralOptionNotifyOnConnectionKey, CBConnectPeripheralOptionNotifyOnDisconnectionKey, and CBConnectPeripheralOptionNotifyOnNotificationKey unconditionally will cause iOS to display system alerts (popups/notifications) to the user whenever these events occur while the app is suspended. This is an intrusive behavior change for existing users of the library who do not expect or want these alerts.\n\nThese options should be opt-in and configurable (e.g., via platform-specific connection options passed from the Dart layer) rather than enabled by default.

    var options: [String: Any] = [:]

@rohitsangwan01

Copy link
Copy Markdown
Contributor

@jonasbark Thank you for the PR and suggestions, Yes i think this should be an Opt-in connect option, and like you mentioned, we should add ios specific options in connect api

Address review feedback on Navideck#264: instead of unconditionally passing the
CBConnectPeripheralOptionNotifyOn*Key options on Apple platforms, expose
them as an opt-in AppleConnectionOptions carried by a new
ConnectionPlatformConfig parameter on connect(), mirroring the existing
PeripheralPlatformConfig pattern. Default behavior is unchanged.

Threads the new parameter through the pigeon definition (regenerated for
Dart/Kotlin/Swift/C++), the platform interface, and all platform
implementations; only iOS/macOS act on it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jonasbark
jonasbark marked this pull request as ready for review July 9, 2026 11:43
Copilot AI review requested due to automatic review settings July 9, 2026 11:43
@jonasbark

Copy link
Copy Markdown
Contributor Author

Done in 9b9ac03 — the notify options are now opt-in and off by default.

connect() takes an optional ConnectionPlatformConfig with Apple-specific AppleConnectionOptions (notifyOnConnection / notifyOnDisconnection / notifyOnNotification), mirroring the PeripheralPlatformConfig pattern. The parameter is threaded through the pigeon connect signature and all platforms; only iOS/macOS act on it. With no config passed, manager.connect is called exactly as before, so existing consumers see no behavior change.

Also updated the README (new "Background connection events (iOS/macOS)" section) and CHANGELOG, and marked the PR ready for review. Happy to adjust naming/shape if you'd prefer something different.

@rohitsangwan01
rohitsangwan01 merged commit ae04b14 into Navideck:main Jul 9, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in, Apple-specific connection configuration so CoreBluetooth can surface connection/disconnection/notification events while an app is suspended (via relaunch into the background), threaded end-to-end through the Dart API and regenerated Pigeon bindings for all supported platforms.

Changes:

  • Add ConnectionPlatformConfig / AppleConnectionOptions and pass it through UniversalBle.connect() (and BleDevice.connect) into the Pigeon connect call.
  • Implement Darwin support by mapping the options to CBConnectPeripheralOptionNotifyOn*Key and consolidating connect option handling into a single manager.connect(..., options:) call.
  • Regenerate Pigeon outputs across Dart/Kotlin/Swift/C++ and plumb the new argument through non-Apple platforms as accepted-but-ignored, plus update docs/changelog.

Reviewed changes

Copilot reviewed 19 out of 21 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
windows/src/universal_ble_plugin.h Updates Windows channel interface Connect signature to accept platform_config.
windows/src/universal_ble_plugin.cpp Accepts platform_config in Windows connect implementation (ignored on Windows).
windows/src/generated/universal_ble.g.h Adds generated C++ Pigeon types for AppleConnectionOptions and ConnectionPlatformConfig and updates connect API signature.
windows/src/generated/universal_ble.g.cpp Implements new generated C++ Pigeon types and updates codec/type IDs + connect message decoding.
test/universal_ble_test_mock.dart Updates test mock platform interface to include platformConfig in connect.
README.md Documents iOS/macOS background connection event options and sample usage.
pigeon/universal_ble.dart Adds new Pigeon model types and extends connect API with platformConfig.
lib/universal_ble.dart Exports the newly generated connection config types from the public library surface.
lib/src/universal_ble.g.dart Regenerated Dart Pigeon output with new types and updated connect message encoding.
lib/src/universal_ble.dart Adds platformConfig to UniversalBle.connect and forwards it into the platform implementation.
lib/src/universal_ble_web/universal_ble_web.dart Accepts (and ignores) platformConfig for web implementation.
lib/src/universal_ble_pigeon/universal_ble_pigeon_channel.dart Forwards platformConfig through the Pigeon channel connect call.
lib/src/universal_ble_linux/universal_ble_linux.dart Accepts (and ignores) platformConfig for Linux implementation.
lib/src/interfaces/universal_ble_platform_interface.dart Extends platform interface connect signature with platformConfig.
lib/src/extensions/ble_device_extension.dart Extends BleDevice.connect to accept and forward platformConfig.
example/lib/data/mock_universal_ble.dart Updates example mock to match the new connect signature.
darwin/universal_ble/Sources/universal_ble/UniversalBlePlugin.swift Implements Darwin connect option dictionary construction and maps AppleConnectionOptions to CoreBluetooth connect options.
darwin/universal_ble/Sources/universal_ble/UniversalBle.g.swift Regenerated Swift Pigeon output including new types and updated connect signature/codec.
android/src/main/kotlin/com/navideck/universal_ble/UniversalBlePlugin.kt Updates Android connect signature to accept platformConfig (ignored on Android).
android/src/main/kotlin/com/navideck/universal_ble/UniversalBle.g.kt Regenerated Kotlin Pigeon output including new types and updated connect signature/codec.
CHANGELOG.md Notes the new opt-in iOS/macOS connection options feature in the release notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CHANGELOG.md
@@ -1,5 +1,6 @@
## 2.1.0
* Add optional `queueId` parameter to all APIs
* iOS/macOS: add opt-in `AppleConnectionOptions` to `connect` (via `ConnectionPlatformConfig`) — map to `CBConnectPeripheralOptionNotifyOnConnectionKey`, `NotifyOnDisconnectionKey` and `NotifyOnNotificationKey`, so a suspended app is relaunched into the background to handle connection events (e.g. auto-reconnect while the phone is locked)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants