kenjdavidson / react-native-bluetooth-classic

⚛ Bluetooth classic Android(Bluetooth)/IOS(ExternalAccessory) module for serial communication
https://kenjdavidson.github.io/react-native-bluetooth-classic
MIT License
249 stars 93 forks source link

onDataReceived() function not working on iOS 17 #285

Closed MobileTallent closed 9 months ago

MobileTallent commented 11 months ago

Mobile Device Environment Provide a list of operating systems on which this issue is relevant.

Application Environment Provide information about your development environment:

device.connect({charset: ".nonLossyASCII"}); when I used above option for the connection. I faced this error: image

device.connect(); when I didn't use the "charset" option for the connection. I faced this error:

image

It seems device.onDataReceived() function not working on iOS 17

kenjdavidson commented 11 months ago

Non lossy is the default now, so maybe don't add it to the connection options?

It's possible the logic for parsing the Charset doesn't like the non lossy value. Did you always pass in non lossy? Or did you just add this to try and resolve with an older version?

MobileTallent commented 11 months ago

Thank you. In the old version, even if I didn't add any options, and it worked fine on iOS 16. On iOS 17, I faced the issue of onDataReceived() not working.

I heard that the Charset non lossy option should be added to resolve the iOS 17 issue, so I added the Charset option. But still failed.

What kind of info do you need to resolve this problem?

kenjdavidson commented 11 months ago

Best case scenerio is you debug it an dope a pr.

I was working with another user who resolved their issue with this fix, you can see his pr in the list, and follow the last issue.

Previously the issue was that ios17 changed how ascii was parsed and it wasn't able to find limiters anymore. Hence why read and listen aren't working, it thinks there's no messages.

You can try utf8 maybe and see if that keeps the newline characters. From my understanding it's now working and the other user has released a working app.

@jim-at-jibba

kenjdavidson commented 11 months ago

https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/5c738e095412ec3306f2192c9efb77c7222534ed/ios/conn/DelimitedStringDeviceConnectionImpl.swift#L74C1-L77C103

the lines here are probably way wrong in how they attempt to convert the .nonLossyASCII string into the CFStringEncoding type, which looks like it's wrong. My lack of experience with Swift is the main culprit, and it looks like only .utf8 will work this way (ha/oops).

If you are more comfortable with swift, feel free to update that block so that the Encoding enum is parsed correctly.

MobileTallent commented 11 months ago

The default encoding is .nonLossyASCII ?

kenjdavidson commented 11 months ago

Yes, it was changed as a fix to https://github.com/kenjdavidson/react-native-bluetooth-classic/issues/278

MobileTallent commented 11 months ago

What was the older default value?

kenjdavidson commented 11 months ago

The original value was .ascii.

Which in IOS17 would do something funky with the \n character.

jim-at-jibba commented 11 months ago

Hey,

Yep Ken is correct, I could not get it to work with the \n delimiter without it being .nonLossyASCII, It was not parsed correctly in the previous state.

We are not using it in production with the current implementation and all seems fine

MobileTallent commented 11 months ago

But when I use this code, the connection itself did not work.

let device = await RNBluetoothClassic.getBondedDevices(); await device.connect({charset: ".nonLossyASCII"});

kenjdavidson commented 11 months ago

Ya, that's because the code for parsing the connect property is wrong:

        if let value = self.properties["DEVICE_CHARSET"] { self.encoding = String.Encoding.from(value as! CFStringEncoding) }
        else if let value = self.properties["device_charset"] { self.encoding = String.Encoding.from(value as! CFStringEncoding) }
        else if let value = self.properties["charset"] { self.encoding = String.Encoding.from(value as! CFStringEncoding) }
        else { self.encoding = String.Encoding.from(CFStringBuiltInEncodings.nonLossyASCII.rawValue) }

Looking at this it looks like CFStringEncoding is just a remap of utf8 which makes sense as to why those don't actually work. My bad. Looking at some of the Enums in question though:

Essentially I have no idea how to get from string to String.Encoding in Swift? Maybe it's just:

        if let value = self.properties["DEVICE_CHARSET"] { self.encoding = String.Encoding.from(value as! String) }
        else if let value = self.properties["device_charset"] { self.encoding = String.Encoding.from(value as! String) }
        else if let value = self.properties["charset"] { self.encoding = String.Encoding.from(value as! String) }
        else { self.encoding = String.Encoding.from(CFStringBuiltInEncodings.nonLossyASCII.rawValue) }

instead?

kenjdavidson commented 11 months ago

Or maybe

        if let value = self.properties["DEVICE_CHARSET"] { self.encoding = String.Encoding.from(CFStringBuiltInEncodings(rawValue: value)) }
        else if let value = self.properties["device_charset"] { self.encoding = String.Encoding.from(CFStringBuiltInEncodings(rawValue: value)) }
        else if let value = self.properties["charset"] { self.encoding = String.Encoding.from(CFStringBuiltInEncodings(rawValue: value) }
        else { self.encoding = String.Encoding.from(CFStringBuiltInEncodings.nonLossyASCII.rawValue) }

Would be cool if you could give it a go.

MobileTallent commented 11 months ago

Should I make above changes on this file? - DelimitedStringDeviceConnectionImpl.swift

kenjdavidson commented 11 months ago

Feel free to go for it and let me know. The world is your oyster my friend!

MobileTallent commented 11 months ago

My Swift version is 5, but sorry the above code not working on my side

kenjdavidson commented 11 months ago

So you're saying:

  1. You cannot provide the charset parameter in the connect({}) options? If you do, you get an error? What error? The screenshot above doesn't have a specific error (or else I'm blind, which is possible).

  2. When you remove charset from the connect({}) parameters and just have the default .nonLossyASCII pickup, it still doesn't work? What errors do you get?

  3. If you get passed the connect() issue using (1) or (2) above then what happens when you debug the string connection? Are you seeing data coming in through the Stream? Is it the same issue as #278 or is it something new?

You're going to need to do some debugging for me in order to help.

MobileTallent commented 11 months ago

On iOS 17, the DELIMITER is changed into "\n" , or still "\n" ?

  1. When I use only this ({charset: ".nonLossyASCII"}), DelimitedStringDeviceConnectionImpl.init function returns error and crashed.

  2. when I didn't use any options, so the default options will be used, then DelimitedStringDeviceConnectionImpl.read function returns error and crashed.

  3. stream data couldn't be shown, because the read function throws error. I think its a bit similar with the issue https://github.com/kenjdavidson/react-native-bluetooth-classic/issues/278

kenjdavidson commented 11 months ago

The delimiter hasn't changed unless you asked it to change. The default delimiter is \n but you can override this with the connection property connect({ delimiter: "::" }) if you so choose. But chances are you haven't so it's most likely still \n.

  1. ok.
  2. I see the read you're talking about. I still need to get a stack trace in order to see what the actual error is.~If read is throwing an error, can you post what the error is in text? Not just a screenshot that doesn't actually show the error.~

The line in question is:

let numBytesRead = stream.read(buffer, maxLength: readSize)

which is being called when data is present. There are limited posibilities for this:

Need to know which it is. You should be able to debug this and provide more info.

kenjdavidson commented 11 months ago

Hey @MobileTallent any follow up on this?

jim-at-jibba commented 11 months ago

@kenjdavidson hey bud, I will pick this up. Can't promise ETA but will do it as soon as I can

kenjdavidson commented 11 months ago

@jim-at-jibba Is it still not working for you either with the fix you put in for .nonLossyAscii? I thought you said it was working with that last change?

jim-at-jibba commented 11 months ago

Ah no, it's working fine! Been in production and used daily since we merged. It was more the bit about how we set the encoding type in the initialiser, that you thought might be wrong and had asked the other guy to have a stab at.

If you don't think it's worth it I won't do it but happy to have a look if needed.

kenjdavidson commented 11 months ago

Ah, that thing. That does probably need to be fixed, since it's definitely a bug due to my complete lack of swift enum experience! I don't think there's any rush with it, not too many people use the IOS side of this, I think it's only enterprise level, since most others don't have access to MFi devices (in general).

jim-at-jibba commented 11 months ago

OK, well I am happy to fix but busy with Christmas fast approaching so it wont be for a while.

Probably gonna create an updated example app too. Will let you know when its done.

Take it easy

softdev-915 commented 10 months ago

Hi, Ken. Merry Christmas! I have the same error with @MobileTallent. So I tried to follow your guides, but my app is still crashing or the build is failing.

if let value = self.properties["DEVICE_CHARSET"] { self.encoding = String.Encoding.from(value as! String) } else if let value = self.properties["device_charset"] { self.encoding = String.Encoding.from(value as! String) } else if let value = self.properties["charset"] { self.encoding = String.Encoding.from(value as! String) } else { self.encoding = String.Encoding.from(CFStringBuiltInEncodings.nonLossyASCII.rawValue) }

if let value = self.properties["DEVICE_CHARSET"] { self.encoding = String.Encoding.from(CFStringBuiltInEncodings(rawValue: value)) } else if let value = self.properties["device_charset"] { self.encoding = String.Encoding.from(CFStringBuiltInEncodings(rawValue: value)) } else if let value = self.properties["charset"] { self.encoding = String.Encoding.from(CFStringBuiltInEncodings(rawValue: value) } else { self.encoding = String.Encoding.from(CFStringBuiltInEncodings.nonLossyASCII.rawValue) }

Can you help me on how to fix this error?

Thank you

crashlog.txt

kenjdavidson commented 10 months ago

If you've been following the thread youll see that there are two issue here.

The first and I think qhta you're experiencing is that you are attempting to set the Charset on connection. There is a bug with this code that has never worked.

The second is that the original issue has to do with the delimiter and how ascii decoded the /n. This was resolved by changing the default to nonLossyAscii.

So the question is, what exactly is your issue?

Are you setting the Charset during connection? If so don't. Are you removing the Charset, and still not seeing data? I need you to debug to see why. Do you need to be able to change the Charset from nonLossyAscii? If so, the bug needs to get fixed, which you can either open a pr for or wait for me to figure eit out.

kenjdavidson commented 10 months ago

You're specific error is a null pointer while attempting to unwrap an optional.

0   AccuPin                         0x0000000102dd3c4c Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value + 0 (<compiler-generated>:0)
    1   AccuPin                         0x0000000102dd3c4c DelimitedStringDeviceConnectionImpl.read() + 1052 (DelimitedStringDeviceConnectionImpl.swift:176)
    2   AccuPin                         0x0000000102dd4118 DelimitedStringDeviceConnectionImpl.readDataFromStream(_:) + 280 (DelimitedStringDeviceConnectionImpl.swift:256)
    3   AccuPin                         0x0000000102dd3fe0 @objc DelimitedStringDeviceConnectionImpl.stream(_:handle:) + 68 (<compiler-generated>:0)

Looking at the code

Line 256 shows that data came in and was added to the buffer Line 176 is

let content = String(data: inBuffer, encoding: self.encoding)!

Which looks to me like the conversation to string is returning null. You'll have to debug this line and see what the result is of

Inbuffer Encoding The string, why's it null?

I'm assuming that you actually need utf or something else is up with the data you have in the buffer. But this seems very specific to the GPS device you're using.

You'll need to provide specifics, and maybe I can help.

kenjdavidson commented 10 months ago

Looking at the bad elf sdk, it doesn't look like they use a delimiter that I can see. But they definitely use nonlossyascii. This is super confusing how this library even worked originally with no delimiter sent by the device.

https://github.com/BadElf/gps-sdk/blob/master/samples/ios/swift/GPS%20Sample/Controllers/SessionController.swift

softdev-915 commented 10 months ago

Thank you for your reply. Let me explain in detail. My app was working well under iOS 16. But on iOS 17, an error occurred - GPS data was not captured. My app was using "react-native-bluetooth-classic": "^1.60.0-rc.29". After researching, I upgraded the version to "^1.60.0-rc.30". After upgrading, my app crashed when captured the GPS data. I didn't use any connection option - badElfDevice.connect(). I already attached the error log. Here is how my app crashed.

https://github.com/kenjdavidson/react-native-bluetooth-classic/assets/73112382/a96640a2-cbdf-4608-a605-91a844035e3c

kenjdavidson commented 10 months ago

Yup I fully understand. But you'll need to debug it. I've given you line numbers and background details. I cannot do anything more than this until you provide the debugging. You should have enough details to debug and see what is going on.

softdev-915 commented 10 months ago

Ok. I'll debug and let you know. Thank you

softdev-915 commented 10 months ago

Hello @MobileTallent. I have the same error as you. Were you successful in fixing your issue?

kenjdavidson commented 9 months ago

Any word on this? If there is more information, feel free to re-open when available.

MobileTallent commented 9 months ago

hi @leon-915 I didn't fix the issue. please share the list of debugged datasets

softdev-915 commented 9 months ago

Fixed the issue finally. I changed the code like this: connect({ delimiter: "\r\n" })

Thank you

kenjdavidson commented 9 months ago

Feel free to send me some Cash for this. Nothing big. Just something that shows how. Much I helped for it only to be the first thing I asked. Thanks.