Skip to content

Commit

Permalink
Fix Swift 6 concurrency issues in DarwinGATT
Browse files Browse the repository at this point in the history
  • Loading branch information
colemancda committed Nov 14, 2024
1 parent 11e7e3e commit 852f70b
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 51 deletions.
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ var package = Package(
package: "Bluetooth",
condition: .when(platforms: [.macOS])
)
]
],
swiftSettings: [.swiftLanguageMode(.v5)]
),
.testTarget(
name: "GATTTests",
Expand Down
9 changes: 5 additions & 4 deletions Sources/DarwinGATT/DarwinAdvertisementData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@
// Created by Alsey Coleman Miller on 7/15/18.
//

import Foundation
@preconcurrency import Foundation
import Bluetooth
import GATT

#if canImport(CoreBluetooth)

import CoreBluetooth

/// CoreBluetooth Adverisement Data
public struct DarwinAdvertisementData: AdvertisementData {

public typealias Data = Foundation.Data

// MARK: - Properties

internal let data: [String: NSObject]
Expand Down Expand Up @@ -69,10 +70,10 @@ public extension DarwinAdvertisementData {
}

/// The Manufacturer data of a peripheral.
var manufacturerData: ManufacturerSpecificData? {
var manufacturerData: ManufacturerSpecificData<Foundation.Data>? {

guard let manufacturerDataBytes = data[CBAdvertisementDataManufacturerDataKey] as? Data,
let manufacturerData = ManufacturerSpecificData(data: manufacturerDataBytes)
let manufacturerData = ManufacturerSpecificData<Data>(data: manufacturerDataBytes)
else { return nil }

return manufacturerData
Expand Down
8 changes: 4 additions & 4 deletions Sources/DarwinGATT/DarwinAttributes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ internal protocol CoreBluetoothAttributeConvertible {
func toCoreBluetooth() -> CoreBluetoothPeripheralType
}

extension GATTAttribute.Service: CoreBluetoothAttributeConvertible {
extension GATTAttribute.Service: CoreBluetoothAttributeConvertible where Data == Foundation.Data {

func toCoreBluetooth() -> CBMutableService {

let service = CBMutableService(type: CBUUID(uuid), primary: primary)
let service = CBMutableService(type: CBUUID(uuid), primary: isPrimary)
service.characteristics = characteristics.map { $0.toCoreBluetooth() }
return service
}
}

extension GATTAttribute.Characteristic: CoreBluetoothAttributeConvertible {
extension GATTAttribute.Characteristic: CoreBluetoothAttributeConvertible where Data == Foundation.Data {

func toCoreBluetooth() -> CBMutableCharacteristic {

Expand All @@ -53,7 +53,7 @@ extension GATTAttribute.Characteristic: CoreBluetoothAttributeConvertible {
}
}

extension GATTAttribute.Descriptor: CoreBluetoothAttributeConvertible {
extension GATTAttribute.Descriptor: CoreBluetoothAttributeConvertible where Data == Foundation.Data {

func toCoreBluetooth() -> CBMutableDescriptor {

Expand Down
2 changes: 1 addition & 1 deletion Sources/DarwinGATT/DarwinBluetoothState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import CoreBluetooth
/// Darwin Bluetooth State
///
/// - SeeAlso: [CBManagerState](https://developer.apple.com/documentation/corebluetooth/cbmanagerstate).
@objc public enum DarwinBluetoothState: Int {
@objc public enum DarwinBluetoothState: Int, Sendable, CaseIterable {

case unknown
case resetting
Expand Down
8 changes: 4 additions & 4 deletions Sources/DarwinGATT/DarwinCentral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Bluetooth
import GATT

@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
public final class DarwinCentral: CentralManager, ObservableObject {
public final class DarwinCentral: CentralManager, ObservableObject, @unchecked Sendable {

// MARK: - Properties

Expand Down Expand Up @@ -340,7 +340,7 @@ public final class DarwinCentral: CentralManager, ObservableObject {
// MARK - Private Methods

@inline(__always)
private func async(_ body: @escaping () -> ()) {
private func async(_ body: @escaping @Sendable () -> ()) {
queue.async(execute: body)
}

Expand Down Expand Up @@ -574,7 +574,7 @@ fileprivate extension DarwinCentral.PeripheralContinuationContext {

internal extension DarwinCentral {

final class QueuedOperation {
final class QueuedOperation: @unchecked Sendable {

let operation: DarwinCentral.Operation

Expand Down Expand Up @@ -654,7 +654,7 @@ internal extension DarwinCentral.Operation {

internal protocol DarwinCentralOperation {

associatedtype Success
associatedtype Success: Sendable

var continuation: PeripheralContinuation<Success, Error> { get }

Expand Down
86 changes: 50 additions & 36 deletions Sources/DarwinGATT/DarwinPeripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import Dispatch
import Bluetooth
import BluetoothGATT
import GATT
import CoreBluetooth
@preconcurrency import CoreBluetooth
import CoreLocation

public final class DarwinPeripheral: PeripheralManager {
public final class DarwinPeripheral: PeripheralManager, @unchecked Sendable {

// MARK: - Properties

Expand Down Expand Up @@ -46,11 +46,11 @@ public final class DarwinPeripheral: PeripheralManager {
}
}

public var willRead: ((GATTReadRequest<Central>) async -> ATTError?)?
public var willRead: ((GATTReadRequest<Central, Data>) async -> ATTError?)?

public var willWrite: ((GATTWriteRequest<Central>) async -> ATTError?)?
public var willWrite: ((GATTWriteRequest<Central, Data>) async -> ATTError?)?

public var didWrite: ((GATTWriteConfirmation<Central>) async -> ())?
public var didWrite: ((GATTWriteConfirmation<Central, Data>) async -> ())?

public var stateChanged: ((DarwinBluetoothState) -> ())?

Expand Down Expand Up @@ -105,7 +105,7 @@ public final class DarwinPeripheral: PeripheralManager {
}
}

public func add(service: GATTAttribute.Service) async throws -> (UInt16, [UInt16]) {
public func add(service: GATTAttribute<Data>.Service) async throws -> (UInt16, [UInt16]) {
let serviceObject = service.toCoreBluetooth()
// add service
try await withCheckedThrowingContinuation { [unowned self] (continuation: CheckedContinuation<(), Error>) in
Expand Down Expand Up @@ -321,7 +321,7 @@ extension DarwinPeripheral.Options: CustomStringConvertible {

public extension DarwinPeripheral {

struct AdvertisingOptions: Equatable, Hashable {
struct AdvertisingOptions: Equatable, Hashable, @unchecked Sendable {

internal let options: [String: NSObject]

Expand Down Expand Up @@ -403,8 +403,9 @@ internal extension DarwinPeripheral {

internal extension DarwinPeripheral {

@preconcurrency
@objc(DarwinPeripheralDelegate)
final class Delegate: NSObject, CBPeripheralManagerDelegate {
final class Delegate: NSObject, CBPeripheralManagerDelegate, @unchecked Sendable {

unowned let peripheral: DarwinPeripheral

Expand Down Expand Up @@ -498,33 +499,40 @@ internal extension DarwinPeripheral {
log("Did receive write requests for \(requests.map { $0.characteristic.uuid })")
assert(requests.isEmpty == false)

Task {
var writeRequests = [GATTWriteRequest<Central>]()
writeRequests.reserveCapacity(requests.count)
guard let firstRequest = requests.first else {
assertionFailure()
return
}

let writeRequests: [GATTWriteRequest<Central, Data>] = requests.map { request in
let peer = Central(request.central)
let characteristic = self.peripheral.database[characteristic: request.characteristic]
let value = characteristic.value
let uuid = BluetoothUUID(request.characteristic.uuid)
let newValue = request.value ?? Data()
return GATTWriteRequest(
central: peer,
maximumUpdateValueLength: request.central.maximumUpdateValueLength,
uuid: uuid,
handle: characteristic.handle,
value: value,
newValue: newValue
)
}

let task = Task {

// validate write requests
for request in requests {
let peer = Central(request.central)
let characteristic = self.peripheral.database[characteristic: request.characteristic]
let value = characteristic.value
let uuid = BluetoothUUID(request.characteristic.uuid)
let newValue = request.value ?? Data()
let writeRequest = GATTWriteRequest(
central: peer,
maximumUpdateValueLength: request.central.maximumUpdateValueLength,
uuid: uuid,
handle: characteristic.handle,
value: value,
newValue: newValue
)
for writeRequest in writeRequests {

// check if write is possible
if let error = await self.peripheral.willWrite?(writeRequest) {
peripheral.queue.async {
peripheralManager.respond(to: requests[0], withResult: CBATTError.Code(rawValue: Int(error.rawValue))!)
guard let code = CBATTError.Code(rawValue: Int(error.rawValue)) else {
assertionFailure("Invalid CBATTError: \(error.rawValue)")
return CBATTError.Code.unlikelyError
}
return
return code
}
// compute new data
writeRequests.append(writeRequest)
}

// write new values
Expand All @@ -542,8 +550,14 @@ internal extension DarwinPeripheral {
await self.peripheral.didWrite?(confirmation)
}

peripheral.queue.async {
peripheralManager.respond(to: requests[0], withResult: .success)
return CBATTError.Code.success
}

let queue = self.peripheral.queue
Task {
let result = await task.value
queue.async {
peripheralManager.respond(to: firstRequest, withResult: result)
}
}
}
Expand All @@ -569,14 +583,14 @@ internal extension DarwinPeripheral {

private extension DarwinPeripheral {

struct Database {
struct Database: Sendable {

struct Service {
struct Service: Sendable {

let handle: UInt16
}

struct Characteristic {
struct Characteristic: Sendable {

let handle: UInt16

Expand All @@ -603,7 +617,7 @@ private extension DarwinPeripheral {
return lastHandle
}

mutating func add(service: GATTAttribute.Service, _ coreService: CBMutableService) -> (UInt16, [UInt16]) {
mutating func add(service: GATTAttribute<Data>.Service, _ coreService: CBMutableService) -> (UInt16, [UInt16]) {

let serviceHandle = newHandle()
var characteristicHandles = [UInt16]()
Expand Down
2 changes: 1 addition & 1 deletion Sources/DarwinGATT/PeripheralContinuation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Bluetooth
import GATT

#if DEBUG
internal struct PeripheralContinuation<T, E> where E: Error {
internal struct PeripheralContinuation<T, E> where T: Sendable, E: Error {

private let function: String

Expand Down

0 comments on commit 852f70b

Please sign in to comment.