Skip to content

Commit

Permalink
[Feature] iOS: initial support for Restore State
Browse files Browse the repository at this point in the history
  • Loading branch information
chipweinberger committed Aug 27, 2024
1 parent 1264f68 commit 12c17a6
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 14 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,11 @@ Add the following to your `Info.plist`

When this key-value pair is included in the app’s Info.plist file, the system wakes up your app to process ble `read`, `write`, and `subscription` events.

You may also have to use https://pub.dev/packages/workmanager
To wake up your app even after it is killed by the OS, set the `restoreIosState` option to true **before** starting any FBP work**:

```
FlutterBluePlus.setOptions(restoreIosState: true);
```

**Note**: Upon being woken up, an app has around 10 seconds to complete a task. Apps that spend too much time executing in the background can be throttled back by the system or killed.

Expand Down
73 changes: 62 additions & 11 deletions ios/Classes/FlutterBluePlusPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ @interface FlutterBluePlusPlugin ()
@property(nonatomic) NSTimer *checkForMtuChangesTimer;
@property(nonatomic) LogLevel logLevel;
@property(nonatomic) NSNumber *showPowerAlert;
@property(nonatomic) NSNumber *restoreState;
@end

@implementation FlutterBluePlusPlugin
Expand All @@ -75,6 +76,7 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar
instance.scanCounts = [NSMutableDictionary new];
instance.logLevel = LDEBUG;
instance.showPowerAlert = @(YES);
instance.restoreState = @(NO);

[registrar addMethodCallDelegate:instance channel:methodChannel];
}
Expand Down Expand Up @@ -104,10 +106,19 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
{
Log(LDEBUG, @"handleMethodCall: %@", call.method);

if ([@"setLogLevel" isEqualToString:call.method])
{
NSNumber *idx = [call arguments];
self.logLevel = (LogLevel)[idx integerValue];
result(@YES);
return;
}

if ([@"setOptions" isEqualToString:call.method])
{
NSDictionary *args = (NSDictionary*) call.arguments;
self.showPowerAlert = args[@"show_power_alert"];
self.restoreState = args[@"restore_state"];
result(@YES);
return;
}
Expand All @@ -117,11 +128,18 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
{
Log(LDEBUG, @"initializing CBCentralManager");

NSDictionary *options = @{
CBCentralManagerOptionShowPowerAlertKey: self.showPowerAlert
};
NSMutableDictionary *options = [NSMutableDictionary dictionary];

Log(LDEBUG, @"show power alert: %@", [self.showPowerAlert boolValue] ? @"yes" : @"no");
if ([self.showPowerAlert boolValue]) {
options[CBCentralManagerOptionShowPowerAlertKey] = self.showPowerAlert;
}

if ([self.restoreState boolValue]) {
options[CBCentralManagerOptionRestoreIdentifierKey] = @"flutterBluePlusRestoreIdentifier";
}

Log(LDEBUG, @"showPowerAlert: %@", [self.showPowerAlert boolValue] ? @"yes" : @"no");
Log(LDEBUG, @"restoreState: %@", [self.restoreState boolValue] ? @"yes" : @"no");

self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];
}
Expand Down Expand Up @@ -185,13 +203,6 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
result(@(self.connectedPeripherals.count));
return;
}
else if ([@"setLogLevel" isEqualToString:call.method])
{
NSNumber *idx = [call arguments];
self.logLevel = (LogLevel)[idx integerValue];
result(@YES);
return;
}
else if ([@"isSupported" isEqualToString:call.method])
{
result(self.centralManager != nil ? @(YES) : @(NO));
Expand Down Expand Up @@ -1026,6 +1037,46 @@ - (void)checkForMtuChangesCallback
// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
// ██████ ███████ ███████ ███████ ██████ ██ ██ ██ ███████

- (void)centralManager:(CBCentralManager *)central
willRestoreState:(NSDictionary *)state {

// restore adapter state
[self centralManagerDidUpdateState:central];

NSArray *peripherals = state[CBCentralManagerRestoredStatePeripheralsKey];

for (CBPeripheral *peripheral in peripherals) {

// Set the delegate to self to receive the peripheral callbacks
peripheral.delegate = self;

// skip if not connected
if (peripheral.state != CBPeripheralStateConnected) {
continue;
}

// update connection state
[self centralManager:central didConnectPeripheral:peripheral];

for (CBService *service in peripheral.services) {

// restore services
[self peripheral:peripheral didDiscoverServices:nil];

for (CBCharacteristic *characteristic in service.characteristics) {

// restore characteristics
[self peripheral:peripheral didDiscoverCharacteristicsForService:service error:nil];

// restore notidications
if (characteristic.isNotifying) {
[self peripheral:peripheral didUpdateNotificationStateForCharacteristic:characteristic error:nil];
}
}
}
}
}

- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central
{
Log(LDEBUG, @"centralManagerDidUpdateState %@", [self cbManagerStateString:self.centralManager.state]);
Expand Down
8 changes: 6 additions & 2 deletions lib/src/flutter_blue_plus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,14 @@ class FlutterBluePlus {
/// To set this option you must call this method before any other method in this package.
/// See: https://developer.apple.com/documentation/corebluetooth/cbcentralmanageroptionshowpoweralertkey
/// This option has no effect on Android.
/// - [restoreState] Whether to opt into state restoration (iOS & MacOS only). i.e. CBCentralManagerOptionRestoreIdentifierKey
/// To set this option you must call this method before any other method in this package.
/// See Apple Documentation for more details. This option has no effect on Android.
static Future<void> setOptions({
bool showPowerAlert = true,
bool restoreState = false,
}) async {
await _invokeMethod('setOptions', {"show_power_alert": showPowerAlert});
await _invokeMethod('setOptions', {"show_power_alert": showPowerAlert, "restore_state": restoreState});
}

/// Turn on Bluetooth (Android only),
Expand Down Expand Up @@ -592,7 +596,7 @@ class FlutterBluePlus {

try {
// initialize
if (method != "setOptions") {
if (method != "setOptions" && method != "setLogLevel") {
_initFlutterBluePlus();
}

Expand Down

0 comments on commit 12c17a6

Please sign in to comment.