Reedyuk / blue-falcon

A Bluetooth kotlin multiplatform "Cross-Platform" library for iOS and Android
https://bluefalcon.dev
Apache License 2.0
321 stars 43 forks source link

Kotlin KMM dev.bluefalcon.BluetoothUnknownException: Unknown error happened #87

Closed nilskasseckert closed 2 years ago

nilskasseckert commented 2 years ago

Describe the bug I have included the library via KMM. I have added the dependency in commonMain. Now I built a wrapper around the class in shared code in Kotlin. This looks like this:

class BluetoothManager(private val blueFalcon: BlueFalcon) {
    private val bluetoothDelegate = BluetoothManagerDelegate()

    init {
        blueFalcon.delegates.add(bluetoothDelegate)
    }

    override fun isScanning(): Boolean {
        return blueFalcon.isScanning
    }

    override fun startScanning() {
        if (isScanning()) {
            return
        }

        blueFalcon.scan()
    }

....
}

In iOS I start the scanning in the ViewModel via the following code:

init() {

        bluetoothService = BluetoothManager(blueFalcon: Blue_falconBlueFalcon(context: UIView(), serviceUUID: nil))

        bluetoothService.bluetoothManager.startScanning() --> The Crash is happening here
        isScanning = bluetoothService.bluetoothManager.isScanning()
    }
}

The app crashes immediately. Why? The crash report contains following:

Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
Uncaught Kotlin exception: dev.bluefalcon.BluetoothUnknownException: Unknown error happened
    at 0   shared                              0x108b2e1db        kfun:kotlin.Throwable#<init>(kotlin.String?){} + 99 
    at 1   shared                              0x108b26d0f        kfun:kotlin.Exception#<init>(kotlin.String?){} + 95 
    at 2   shared                              0x108f0f86b        kfun:dev.bluefalcon.BluetoothUnknownException#<init>(kotlin.String){} + 95 
    at 3   shared                              0x108f0f957        kfun:dev.bluefalcon.BluetoothUnknownException#<init>(kotlin.String?;kotlin.Int;kotlin.native.internal.DefaultConstructorMarker?){} + 215 
    at 4   shared                              0x108f151bb        kfun:dev.bluefalcon.BlueFalcon#scan(){} + 491 
    at 5   shared                              0x108f2a28b        kfun:eu.ces.connect_multi.manager.bluetooth.BluetoothManager#startScanning(){} + 171 
    at 6   shared                              0x10907b307        objc2kotlin.3043 + 135 

To Reproduce Ue the same code as I

Expected behavior That all is working

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Reedyuk commented 2 years ago

Would like to just check one important thing, is this when running a simulator? or a device?

nilskasseckert commented 2 years ago

When running on device. Bluetooth permission is granted

Reedyuk commented 2 years ago

Then its not an issue because bluetooth is not supported on simulators.

nilskasseckert commented 2 years ago

DEVICE i Wrote

The issue is on DEVICE

nilskasseckert commented 2 years ago

NOT SIMULATOR!!

Please reopen

Reedyuk commented 2 years ago

its probably because apple changed enum for errors in ios 15. Have you tried to debug the issue? This is an open source project, so happy for you to produce a pr to resolve the issue.

nilskasseckert commented 2 years ago

Why do you think this is an problem with enums?

The error is "BluetoothUnkownError"

And the app crash because I don't handle the issue. But my question is: What does "BluetoothUnkownError" mean.

Debugging an KMM Application is unfortanly a litte bit complicated...

Reedyuk commented 2 years ago

if you look at the stacktrace that you posted, you can track it back to where its happening in the kotlin code.

    at 0   shared                              0x108b2e1db        kfun:kotlin.Throwable#<init>(kotlin.String?){} + 99 
    at 1   shared                              0x108b26d0f        kfun:kotlin.Exception#<init>(kotlin.String?){} + 95 
    at 2   shared                              0x108f0f86b        kfun:dev.bluefalcon.BluetoothUnknownException#<init>(kotlin.String){} + 95 
    at 3   shared                              0x108f0f957        kfun:dev.bluefalcon.BluetoothUnknownException#<init>(kotlin.String?;kotlin.Int;kotlin.native.internal.DefaultConstructorMarker?){} + 215 
    at 4   shared                              0x108f151bb        kfun:dev.bluefalcon.BlueFalcon#scan(){} + 491 
    at 5   shared                              0x108f2a28b        kfun:eu.ces.connect_multi.manager.bluetooth.BluetoothManager#startScanning(){} + 171 

Its happening in the function BlueFalcon.scan() and creates an exception called BluetoothUnknownException. If you look at the ios target source code of the lib.

https://github.com/Reedyuk/blue-falcon/blob/20d886522368d14ceb748a11345657d9e06cefb0/library/src/iosMain/kotlin/dev/bluefalcon/BlueFalcon.kt#L42

when (centralManager.state) {
            CBManagerStateUnknown -> throw BluetoothUnknownException()
            CBManagerStateResetting -> throw BluetoothResettingException()
            CBManagerStateUnsupported -> throw BluetoothUnsupportedException()
            CBManagerStateUnauthorized -> throw BluetoothPermissionException()
            CBManagerStatePoweredOff -> throw BluetoothNotEnabledException()
            CBManagerStatePoweredOn -> {
                if (serviceUUID != null) {
                    val serviceCBUUID = CBUUID.UUIDWithString(serviceUUID)
                    centralManager.scanForPeripheralsWithServices(listOf(serviceCBUUID), null)
                } else {
                    centralManager.scanForPeripheralsWithServices(null, null)
                }
            }
        }

You will actually see that CoreBluetooth returns the state enum CBManagerStateUnknown, so the actual error resides within CoreBluetooth within ios, so i would suggest you look for issues related to this. The library has actually reported the correct error and that the error lives in iOS source code so closing this issue.

nilskasseckert commented 2 years ago

But your library is Using this and initializing this centralManager? It seems that this does not work correctly.

Why do I need for example an view when I initializing the blue falcon?

See here:

Blue_falconBlueFalcon(context: UIView(), serviceUUID: nil))
Reedyuk commented 2 years ago

central manager is an instance of

centralManager = CBCentralManager(bluetoothPeripheralManager, null)

CoreBluetooth - again back to an apple foundation library.

The context is useless, it probably should be nullable to be honest, it was put in place to have a common constructor between Android and iOS.

nilskasseckert commented 2 years ago

I try to debug it. But the error seems to be not an iOS error.

If I catch the exception I get following: Unknown error happenedState 5 is .poweredOn

I use bluetooth since many years but I have never seen this issue in a foundation lib of apple.

If I afterwards call the function: blueFalcon.isScanning I get the result: True

But the scan does nothing.

I use the lib from KMM. In your examples you use the framework directly. I implement the framework in kotlin shared code and only call it from iOS.

Reedyuk commented 2 years ago

Hmm strange,you would of thought that it would be under the status enum

CBManagerStatePoweredOn

So not sure why it would fall under unknown.

I was not able to reproduce in the example projects, do you have a repo where I can reproduce your issue? You can give me private access if it is sensitive.

nilskasseckert commented 2 years ago

Hi,

thanks for the fast answer! I created a new repo. Because I removed everything private it can be public and found here: https://github.com/AppSupporter/bluetooth-bug

Steps to reproduce the issue:

  1. Clone the project
  2. Open Xcode Workspace in iOSApp
  3. Build the Project
  4. Run the App. At the first start you get perhaps only "unknown error". This is because of the missing permission. But after you click on allow for bluetooth or run the app for the second time the "correct" error message appears.

Very strange bug. Normally I would say something with the enum is wrong. But the library works if I use your example without KMM

Thanks for your help! Nils

Reedyuk commented 2 years ago

Ah i know the issue now.

You cannot perform a scan immediately after instantiating a BlueFalcon object in iOS, this is due to the way corebluetooth works. Related to this issue: https://stackoverflow.com/questions/25932366/cbmanager-state-always-unknown

I have fixed your example where its not working to make the scan fire afterwards, with a scan button. https://github.com/AppSupporter/bluetooth-bug/pull/1

This is not really a bug with blue falcon, more an issue with the way core bluetooth works, unfortunately. I am going to close this issue.

nilskasseckert commented 2 years ago

Thanks a lot!