Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

init CentralManager with restorationId causes unsupported state #264

Closed
ghost opened this issue May 30, 2018 · 12 comments
Closed

init CentralManager with restorationId causes unsupported state #264

ghost opened this issue May 30, 2018 · 12 comments

Comments

@ghost
Copy link

ghost commented May 30, 2018

I'm facing a weird issue (version 5.1.0 of this lib):
When using the CentralManager without any init option, the observeState() observable is well working.

But as soon as I add a restore identifier key, the observeState() observable just sends one (and only one) unsupported state, then no more events even after enabling/disabling Bluetooth (either from control center or Settings).
As soon as I remove the restoreIdentifier options, it works back again nicely...

Is there anything wrong I did, or an issue in the lib? Thanks for the help!

Here is the observe state part that prints everything happening there:

public func observeBluetoothState() -> Observable<BluetoothState> {
        return manager!.observeState()
            .do(
                onNext: { newstate in print("RXBT observeState onNext, new state is \(newstate)") },
                onError: { error in print("RXBT observeState onError, error is \(error)") },
                onCompleted: { print("RXBT observeState onCompleted") },
                onSubscribe: { print("RXBT observeState onSubscribe") },
                onSubscribed: { print("RXBT observeState onSubscribed") },
                onDispose: { print("RXBT observeState onDispose") }
            )
    }

CentralManager initialization without options:

var manager = CentralManager(  queue: .main )

/* result output: 
RXBT observeState onSubscribe
RXBT observeState onSubscribed
RXBT observeState onNext, new state is poweredOn
RXBT observeState onNext, new state is poweredOff
RXBT observeState onNext, new sRXBT observeState onNext, new state is poweredOff
RXBT observeState onNext, new state is poweredOn
RXBT observeState onNext, new state is poweredOff
RXBT observeState onNext, new state is poweredOn
*/

CentralManager initialization with restore options:

var manager = CentralManager(  queue: .main,
                                   options: [CBCentralManagerOptionRestoreIdentifierKey:"get.myId.centralManagerRestoreKey" as AnyObject],
                                   onWillRestoreCentralManagerState: {
                                            centralManagerRestoredState in
                                            print("RXBT centralManagerRestoredState is \(centralManagerRestoredState)")
                                            // do any stuff here later... 
                                        }
        )

/* result output: 
RXBT observeState onSubscribe
RXBT observeState onSubscribed
RXBT observeState onNext, new state is unsupported
*/
@ghost ghost closed this as completed May 30, 2018
@ghost ghost reopened this May 30, 2018
@pouljohn1
Copy link
Contributor

@driveorphone Could you tell me on what device are you trying to do this? I've tested it on my iPhone and it works without a problem.
Generally unsupported could mean that your device doesn't support background mode

@ghost
Copy link
Author

ghost commented May 30, 2018

@paweljaneczek sorry didn't mention I'm testing it on an iPhone 6s with iOS 11.3.1
The project is build with XCode 9.3.1 and Swift 4...
We have the background authorization for Uses Bluetooth LE Accessories... (it wakes up the app in background when Bluetooth is turned on/off and when a Bluetooth event occurs)... so it looks like the authorization is well setup towards iOS.

@pouljohn1
Copy link
Contributor

pouljohn1 commented May 30, 2018

@driveorphone By saying Uses Bluetooth LE Accessories you mean that you've added bluetooth-central to UIBackgroundModes in plist file?
Could you please try to do this with just using CoreBluetooth, I just want to figure if this problem is on our side? To do this do:

class TestClass: CBCentralManagerDelegate {
   configureCentralManager() {
       manager = CBCentralManager(delegate: self, queue: .main, options: [CBCentralManagerOptionRestoreIdentifierKey:"get.myId.centralManagerRestoreKey" as AnyObject])
       print("state \(central.state)")
   }

  centralManagerDidUpdateState(_ central: CBCentralManager) {
      print("newState \(central.state)")
  }
}

@ghost
Copy link
Author

ghost commented May 30, 2018

@paweljaneczek indeed I'm using bluetooth-central as UIBackgroundMode in plist...
I tested dealing directly with CBCentralManager as you propose, and it works, the bluetooth state is well written in console.
However, notice that if I do not include the willRestoreStatedelegate it makes the app crash. Would it be possible that I do not use properly onWillRestoreCentralManagerState? What would be an example of correct implementation of it with RxBluetoothKit? (my code below for the record)

var manager = CentralManager(  queue: .main,
   options: [CBCentralManagerOptionRestoreIdentifierKey:"get.myId.centralManagerRestoreKey" as AnyObject],
   onWillRestoreCentralManagerState: {
      centralManagerRestoredState in
      print("RXBT centralManagerRestoredState is \(centralManagerRestoredState)")
      // do any stuff here later... 
   }
)

@ghost
Copy link
Author

ghost commented Jun 1, 2018

At least I could reproduce this issue with CBCentralManager itself: still no idea where it can come from but it means that your lib might not be in cause. Closing that ticket then... And thank you for your time + for this awesome lib!

@ghost ghost closed this as completed Jun 1, 2018
@ghost
Copy link
Author

ghost commented Jun 2, 2018

For whom it could help, I found that just by forcing unwrapping type of CentralManager at initialization did solve the issue. Although I don't really understand what it involves behind the scene, worth to mention it.

This does not work:

var manager = CentralManager(  queue: .main,
   /* etc. etc. */
)

This does work:

var manager:CentralManager! = CentralManager(  queue: .main,
    /* etc. etc. */
)

@aksswami
Copy link

Hi @paweljaneczek @driveorphone, I am facing the same issue. Even after I dispose the scanning output and restart the scanning, I get unsupported state always with the similar setup. If I remove all the options from CenrtalManager initializer, it works fine.

@ghost
Copy link
Author

ghost commented Aug 23, 2018

@aksswami I could note that it happens more often on iPhones <= 6s, and much less often on iPhones >= 7.
My workaround is to put the Bluetooth status observable into a disposable : when I get any strange status I dispose it, wait for a few seconds, and restart the observer. And so on for a maximum of 5 times (or whatever you think is better)... I always end up by getting the right Bluetooth state at the second or the third try... That is not the best solution but giving my 50-cent if it can help you...

@aksswami
Copy link

Thanks for suggestions @driveorphone. We don't face this problem when we remove the restoreKey. Can you elaborate on the downside of doing that? I still don't get the complete idea of restoration. As anyway, we do a peripheral scan and connect each time we want to do something with any peripheral.

Also, there are not methods or docs explaining the restoration process in RxBluetoothKit.

@ghost
Copy link
Author

ghost commented Aug 25, 2018

@aksswami you need the restoration feature only if you need to ensure a future connexion to your device while your application will be backgrounded and/or terminated by the system.
If you only need to connect to you device while your app is used by the user, you don't need to bother with restoration.

The way it works in a few words is :

  1. I ask iOS to scan for a specific advertising BLE service or to connect to a specific peripheral...
  2. Then I am backgrounded and/or terminated by iOS...
  3. Anytime later... when the iPhone will see this device you were looking for, then iOS comes back to wake you up magically, delivering you all the infos you were looking for, and allowing you to act in consequence (save some data, perform some actions, etc.) : it "restores" your centralManager and CBPeripheral, like if they had been put into a freezer in the meantime.

@aksswami
Copy link

Thanks @driveorphone. This is really helpful. For my purpose, I don't require restoration as I am only using peripheral when the application is in the foreground.

But I would really like to debug this issue. Why are we having this state issue when using restoration key. May post here, in case I have something. Thanks again.

@vladiulianbogdan
Copy link
Contributor

I don't know if this is still needed but might be helpful for some people: if you use state restoration, you shouldn't have two CBCentralManagers alive with the same restore identifier. When you create the second one, you will receive state unsupported.

This might happen if you have a leak somewhere and the first CBCentralManager doesn't deallocate correctly.

This issue was closed.
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

No branches or pull requests

3 participants