From f35cadd500caa1468402564483d6c879b513355f Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 7 Jul 2025 18:04:49 +0200 Subject: [PATCH 1/2] fix: corebluetooth canSendWriteWithoutResponse --- src/corebluetooth/internal.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/corebluetooth/internal.rs b/src/corebluetooth/internal.rs index 93597bc7..38824155 100644 --- a/src/corebluetooth/internal.rs +++ b/src/corebluetooth/internal.rs @@ -38,6 +38,7 @@ use std::{ fmt::{self, Debug, Formatter}, ops::Deref, thread, + time::Duration, }; use tokio::runtime; use uuid::Uuid; @@ -887,6 +888,19 @@ impl CoreBluetoothInternal { { trace!("Writing value! With kind {:?}", kind); unsafe { + if kind == WriteType::WithoutResponse { + // probably better idea would be to wait for the result of peripheral.peripheralIsReadyToSendWriteWithoutResponse + let mut attempts = 0; + while !peripheral.peripheral.canSendWriteWithoutResponse() + && attempts < 100 + { + attempts += 1; + // min. connection interval time is 15ms. see the document: + // https://developer.apple.com/library/archive/qa/qa1931/_index.html + thread::sleep(Duration::from_millis(15)); + } + } + peripheral.peripheral.writeValue_forCharacteristic_type( &NSData::from_vec(data), &characteristic.characteristic, From b000925a1a6f421bf6c39de16cba358c6e93b5ef Mon Sep 17 00:00:00 2001 From: Szymon Lesisz Date: Mon, 7 Jul 2025 18:50:59 +0200 Subject: [PATCH 2/2] chore: check is canSendWriteWithoutResponse is supported --- Cargo.toml | 1 + src/corebluetooth/internal.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ed77ba06..964d10e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,7 @@ objc2-foundation = { version = "0.2.2", default-features = false, features = [ "NSString", "NSUUID", "NSValue", + "NSProcessInfo", ] } objc2-core-bluetooth = { version = "0.2.2", default-features = false, features = [ "std", diff --git a/src/corebluetooth/internal.rs b/src/corebluetooth/internal.rs index 38824155..2af0f201 100644 --- a/src/corebluetooth/internal.rs +++ b/src/corebluetooth/internal.rs @@ -31,7 +31,7 @@ use objc2_core_bluetooth::{ CBCharacteristicProperties, CBCharacteristicWriteType, CBDescriptor, CBManager, CBManagerAuthorization, CBManagerState, CBPeripheral, CBPeripheralState, CBService, CBUUID, }; -use objc2_foundation::{NSArray, NSData, NSMutableDictionary, NSNumber}; +use objc2_foundation::{NSArray, NSData, NSMutableDictionary, NSNumber, NSProcessInfo}; use std::{ collections::{BTreeSet, HashMap, VecDeque}, ffi::CString, @@ -369,6 +369,7 @@ impl PeripheralInternal { struct CoreBluetoothInternal { manager: Retained, delegate: Retained, + can_send_without_response_supported: bool, // Map of identifiers to object pointers peripherals: HashMap, delegate_receiver: Fuse>, @@ -492,8 +493,16 @@ impl CoreBluetoothInternal { msg_send_id![CBCentralManager::alloc(), initWithDelegate: &*delegate, queue: queue] }; + let process_info = unsafe { NSProcessInfo::processInfo() }; + let version = unsafe { process_info.operatingSystemVersion() }; + let mut can_send_without_response_supported = false; + if (version.majorVersion, version.minorVersion) >= (11, 2) { + can_send_without_response_supported = true; + } + Self { manager, + can_send_without_response_supported, peripherals: HashMap::new(), delegate_receiver: receiver.fuse(), event_sender, @@ -888,7 +897,9 @@ impl CoreBluetoothInternal { { trace!("Writing value! With kind {:?}", kind); unsafe { - if kind == WriteType::WithoutResponse { + if kind == WriteType::WithoutResponse + && self.can_send_without_response_supported + { // probably better idea would be to wait for the result of peripheral.peripheralIsReadyToSendWriteWithoutResponse let mut attempts = 0; while !peripheral.peripheral.canSendWriteWithoutResponse()