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

Add Embedded Swift support #33

Merged
merged 36 commits into from
Nov 15, 2024
Merged
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
0e60677
Updated dependencies
colemancda Nov 6, 2024
1b35332
Add Embedded Swift support to `AdvertisementData`
colemancda Nov 12, 2024
4aca8e1
Updated dependencies
colemancda Nov 12, 2024
f085ea0
Add `OptionSet` conformance to `AttributePermissions`
colemancda Nov 12, 2024
f9a74e5
Add `OptionSet` conformance to `CharacteristicProperties`
colemancda Nov 12, 2024
ead10d4
Add Embedded Swift support to `ScanData`
colemancda Nov 12, 2024
c7d4588
Add `PeripheralManager.Data`
colemancda Nov 12, 2024
0d8bf76
Add `DataConvertible` support to `ManufacturerSpecificData`
colemancda Nov 12, 2024
6405d10
Add `Sendable` conformance to `GATTCentralAttribute`
colemancda Nov 12, 2024
0846411
Update `AsyncCentralScan`
colemancda Nov 13, 2024
17213bf
Add `CentralManager.Data`
colemancda Nov 13, 2024
ac154f2
Add `Sendable` conformance to `Peer`
colemancda Nov 13, 2024
4c21f4f
Update for Swift 6
colemancda Nov 13, 2024
66cbb4a
Add `Sendable` conformance to `MaximumTransmissionUnit`
colemancda Nov 13, 2024
7004f48
Fixed `AsyncCentralScan`
colemancda Nov 13, 2024
7c69492
Update `GATTClientConnection` for Swift 6
colemancda Nov 14, 2024
68e82d7
Add `Identifiable` conformance to `Peer`
colemancda Nov 14, 2024
11e7e3e
Fix Swift Embedded support for `ScanData`
colemancda Nov 14, 2024
852f70b
Fix Swift 6 concurrency issues in DarwinGATT
colemancda Nov 14, 2024
e8ee5ce
Update dependencies
colemancda Nov 14, 2024
4303c50
Improve concurrency support for `GATTServerConnection`
colemancda Nov 14, 2024
a1fb13d
Update `GATTCentral` for `L2CAPConnection`
colemancda Nov 14, 2024
1984595
Update `GATTPeripheral` for `L2CAPServer`
colemancda Nov 14, 2024
c4fe9e8
Improve Embedded Swift support
colemancda Nov 14, 2024
f860151
Updated unit tests
colemancda Nov 14, 2024
97de3fd
Updated unit tests
colemancda Nov 14, 2024
3dad871
Fixed `GATTPeripheral.didWrite()`
colemancda Nov 14, 2024
e83fb01
Add Embedded Swift support for `PeripheralManager`
colemancda Nov 14, 2024
2906df6
Update VS Code container
colemancda Nov 14, 2024
3502b46
Updated GitHub CI
colemancda Nov 14, 2024
8f2c781
Remove platform deployment requirement
colemancda Nov 15, 2024
f2fa2f9
Updated `DarwinPeripheral` for `PeripheralManager` conformance
colemancda Nov 15, 2024
697126b
Updated unit tests
colemancda Nov 15, 2024
c41faee
Add `PeripheralManager.log`
colemancda Nov 15, 2024
13bd4d1
Fixed `CentralManager.log`
colemancda Nov 15, 2024
198bf60
Add `Identifiable` conformance
colemancda Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix Swift 6 concurrency issues in DarwinGATT
colemancda committed Nov 14, 2024
commit 852f70b9945f6ffeff1de0f21ebd8847535c72ed
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -73,7 +73,8 @@ var package = Package(
package: "Bluetooth",
condition: .when(platforms: [.macOS])
)
]
],
swiftSettings: [.swiftLanguageMode(.v5)]
),
.testTarget(
name: "GATTTests",
9 changes: 5 additions & 4 deletions Sources/DarwinGATT/DarwinAdvertisementData.swift
Original file line number Diff line number Diff line change
@@ -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]
@@ -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
8 changes: 4 additions & 4 deletions Sources/DarwinGATT/DarwinAttributes.swift
Original file line number Diff line number Diff line change
@@ -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 {

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

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

func toCoreBluetooth() -> CBMutableDescriptor {

2 changes: 1 addition & 1 deletion Sources/DarwinGATT/DarwinBluetoothState.swift
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions Sources/DarwinGATT/DarwinCentral.swift
Original file line number Diff line number Diff line change
@@ -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

@@ -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)
}

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

internal extension DarwinCentral {

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

let operation: DarwinCentral.Operation

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

internal protocol DarwinCentralOperation {

associatedtype Success
associatedtype Success: Sendable

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

86 changes: 50 additions & 36 deletions Sources/DarwinGATT/DarwinPeripheral.swift
Original file line number Diff line number Diff line change
@@ -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

@@ -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) -> ())?

@@ -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
@@ -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]

@@ -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

@@ -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
@@ -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)
}
}
}
@@ -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

@@ -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]()
2 changes: 1 addition & 1 deletion Sources/DarwinGATT/PeripheralContinuation.swift
Original file line number Diff line number Diff line change
@@ -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