Skip to content

Commit

Permalink
Merge pull request #98 from dotintent/RxBluetooth-update
Browse files Browse the repository at this point in the history
Update to new RxBluetoothKit
  • Loading branch information
KamilNatonek authored Sep 26, 2023
2 parents b911810 + 46dbede commit 839649c
Show file tree
Hide file tree
Showing 186 changed files with 9,202 additions and 6,538 deletions.
2 changes: 1 addition & 1 deletion iOS/.gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Created by https://www.gitignore.io/api/xcode

## Build generated
#build/
build/
DerivedData/

## Various settings
Expand Down
1,480 changes: 787 additions & 693 deletions iOS/MultiPlatformBLEAdapter.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions iOS/RxBluetoothKit/Array+Utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

extension Array where Element: Equatable {
@discardableResult mutating func remove(object: Element) -> Bool {
if let index = firstIndex(of: object) {
self.remove(at: index)
return true
}
return false
}
}
105 changes: 71 additions & 34 deletions iOS/RxBluetoothKit/BluetoothError.swift
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
// The MIT License (MIT)
//
// Copyright (c) 2016 Polidea
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Foundation
import CoreBluetooth

Expand All @@ -29,28 +7,43 @@ public enum BluetoothError: Error {
/// To mitigate it dispose all of your subscriptions before deinitializing
/// object that created Observables that subscriptions are made to.
case destroyed
// Emitted when `CentralManager.scanForPeripherals` called and there is already ongoing scan
case scanInProgress
// Emitted when `PeripheralManager.startAdvertising` called and there is already ongoing advertisement
case advertisingInProgress
case advertisingStartFailed(Error)
// States
case bluetoothUnsupported
case bluetoothUnauthorized
case bluetoothPoweredOff
case bluetoothInUnknownState
case bluetoothResetting
// Peripheral
case peripheralIsAlreadyObservingConnection(Peripheral)
@available(*, deprecated, renamed: "BluetoothError.peripheralIsAlreadyObservingConnection")
case peripheralIsConnectingOrAlreadyConnected(Peripheral)
case peripheralConnectionFailed(Peripheral, Error?)
case peripheralDisconnected(Peripheral, Error?)
case peripheralRSSIReadFailed(Peripheral, Error?)
// Services
case servicesDiscoveryFailed(Peripheral, Error?)
case includedServicesDiscoveryFailed(Service, Error?)
case includedServicesDiscoveryFailed(Peripheral, Error?)
case addingServiceFailed(CBService, Error?)
// Characteristics
case characteristicsDiscoveryFailed(Service, Error?)
case characteristicWriteFailed(Characteristic, Error?)
case characteristicReadFailed(Characteristic, Error?)
case characteristicNotifyChangeFailed(Characteristic, Error?)
case characteristicSetNotifyValueFailed(Characteristic, Error?)
// Descriptors
case descriptorsDiscoveryFailed(Characteristic, Error?)
case descriptorWriteFailed(Descriptor, Error?)
case descriptorReadFailed(Descriptor, Error?)
// L2CAP
case openingL2CAPChannelFailed(Peripheral, Error?)
case publishingL2CAPChannelFailed(CBL2CAPPSM, Error?)
// Unknown
case unknownWriteType
}

extension BluetoothError: CustomStringConvertible {
Expand All @@ -60,9 +53,21 @@ extension BluetoothError: CustomStringConvertible {
switch self {
case .destroyed:
return """
The object that is the source of this Observable was destroyed.
It's programmer's error, please check documentation of error for more details
"""
The object that is the source of this Observable was destroyed.
It's programmer's error, please check documentation of error for more details
"""
case .scanInProgress:
return """
Tried to scan for peripheral when there is already ongoing scan.
You can have only 1 ongoing scanning, please check documentation of CentralManager for more details
"""
case .advertisingInProgress:
return """
Tried to advertise when there is already advertising ongoing.
You can have only 1 ongoing advertising, please check documentation of PeripheralManager for more details
"""
case let .advertisingStartFailed(err):
return "Start advertising error occured: \(err.localizedDescription)"
case .bluetoothUnsupported:
return "Bluetooth is unsupported"
case .bluetoothUnauthorized:
Expand All @@ -73,19 +78,27 @@ extension BluetoothError: CustomStringConvertible {
return "Bluetooth is in unknown state"
case .bluetoothResetting:
return "Bluetooth is resetting"
// Peripheral
// Peripheral
case .peripheralIsAlreadyObservingConnection, .peripheralIsConnectingOrAlreadyConnected:
return """
Peripheral connection is already being observed.
You cannot try to establishConnection to peripheral when you have ongoing
connection (previously establishConnection subscription was not disposed).
"""
case let .peripheralConnectionFailed(_, err):
return "Connection error has occured: \(err?.localizedDescription ?? "-")"
case let .peripheralDisconnected(_, err):
return "Connection error has occured: \(err?.localizedDescription ?? "-")"
case let .peripheralRSSIReadFailed(_, err):
return "RSSI read failed : \(err?.localizedDescription ?? "-")"
// Services
// Services
case let .servicesDiscoveryFailed(_, err):
return "Services discovery error has occured: \(err?.localizedDescription ?? "-")"
case let .includedServicesDiscoveryFailed(_, err):
return "Included services discovery error has occured: \(err?.localizedDescription ?? "-")"
// Characteristics
case let .addingServiceFailed(_, err):
return "Adding PeripheralManager service error has occured: \(err?.localizedDescription ?? "-")"
// Characteristics
case let .characteristicsDiscoveryFailed(_, err):
return "Characteristics discovery error has occured: \(err?.localizedDescription ?? "-")"
case let .characteristicWriteFailed(_, err):
Expand All @@ -94,13 +107,22 @@ extension BluetoothError: CustomStringConvertible {
return "Characteristic read error has occured: \(err?.localizedDescription ?? "-")"
case let .characteristicNotifyChangeFailed(_, err):
return "Characteristic notify change error has occured: \(err?.localizedDescription ?? "-")"
// Descriptors
case let .characteristicSetNotifyValueFailed(_, err):
return "Characteristic isNotyfing value change error has occured: \(err?.localizedDescription ?? "-")"
// Descriptors
case let .descriptorsDiscoveryFailed(_, err):
return "Descriptor discovery error has occured: \(err?.localizedDescription ?? "-")"
case let .descriptorWriteFailed(_, err):
return "Descriptor write error has occured: \(err?.localizedDescription ?? "-")"
case let .descriptorReadFailed(_, err):
return "Descriptor read error has occured: \(err?.localizedDescription ?? "-")"
case let .openingL2CAPChannelFailed(_, err):
return "Opening L2CAP channel error has occured: \(err?.localizedDescription ?? "-")"
case let .publishingL2CAPChannelFailed(_, err):
return "Publishing L2CAP channel error has occured: \(err?.localizedDescription ?? "-")"
// Unknown
case .unknownWriteType:
return "Unknown write type"
}
}
}
Expand Down Expand Up @@ -130,27 +152,42 @@ extension BluetoothError: Equatable {}

public func == (lhs: BluetoothError, rhs: BluetoothError) -> Bool {
switch (lhs, rhs) {
case (.scanInProgress, .scanInProgress): return true
case (.advertisingInProgress, .advertisingInProgress): return true
case (.advertisingStartFailed, .advertisingStartFailed): return true
// States
case (.bluetoothUnsupported, .bluetoothUnsupported): return true
case (.bluetoothUnauthorized, .bluetoothUnauthorized): return true
case (.bluetoothPoweredOff, .bluetoothPoweredOff): return true
case (.bluetoothInUnknownState, .bluetoothInUnknownState): return true
case (.bluetoothResetting, .bluetoothResetting): return true
// Services
// Services
case let (.servicesDiscoveryFailed(l, _), .servicesDiscoveryFailed(r, _)): return l == r
case let (.includedServicesDiscoveryFailed(l, _), .includedServicesDiscoveryFailed(r, _)): return l == r
// Peripherals
case let (.addingServiceFailed(l, _), .addingServiceFailed(r, _)): return l == r
// Peripherals
case let (.peripheralIsAlreadyObservingConnection(l), .peripheralIsAlreadyObservingConnection(r)): return l == r
case let (.peripheralIsConnectingOrAlreadyConnected(l), .peripheralIsConnectingOrAlreadyConnected(r)): return l == r
case let (.peripheralIsAlreadyObservingConnection(l), .peripheralIsConnectingOrAlreadyConnected(r)): return l == r
case let (.peripheralIsConnectingOrAlreadyConnected(l), .peripheralIsAlreadyObservingConnection(r)): return l == r
case let (.peripheralConnectionFailed(l, _), .peripheralConnectionFailed(r, _)): return l == r
case let (.peripheralDisconnected(l, _), .peripheralDisconnected(r, _)): return l == r
case let (.peripheralRSSIReadFailed(l, _), .peripheralRSSIReadFailed(r, _)): return l == r
// Characteristics
// Characteristics
case let (.characteristicsDiscoveryFailed(l, _), .characteristicsDiscoveryFailed(r, _)): return l == r
case let (.characteristicWriteFailed(l, _), .characteristicWriteFailed(r, _)): return l == r
case let (.characteristicReadFailed(l, _), .characteristicReadFailed(r, _)): return l == r
case let (.characteristicNotifyChangeFailed(l, _), .characteristicNotifyChangeFailed(r, _)): return l == r
// Descriptors
case let (.characteristicSetNotifyValueFailed(l, _), .characteristicSetNotifyValueFailed(r, _)): return l == r
// Descriptors
case let (.descriptorsDiscoveryFailed(l, _), .descriptorsDiscoveryFailed(r, _)): return l == r
case let (.descriptorWriteFailed(l, _), .descriptorWriteFailed(r, _)): return l == r
case let (.descriptorReadFailed(l, _), .descriptorReadFailed(r, _)): return l == r
// L2CAP
case let (.openingL2CAPChannelFailed(l, _), .openingL2CAPChannelFailed(r, _)): return l == r
case let (.publishingL2CAPChannelFailed(l, _), .publishingL2CAPChannelFailed(r, _)): return l == r
// Unknown
case (.unknownWriteType, .unknownWriteType): return true
default: return false
}
}
Expand Down
24 changes: 2 additions & 22 deletions iOS/RxBluetoothKit/BluetoothState.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,7 @@
// The MIT License (MIT)
//
// Copyright (c) 2016 Polidea
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Foundation

/// Type describing bluetooth state, equivalent to
/// [CBManagerState](https://developer.apple.com/documentation/corebluetooth/cbmanagerstate).
public enum BluetoothState: Int {
case unknown
case resetting
Expand Down
70 changes: 48 additions & 22 deletions iOS/RxBluetoothKit/Boxes.swift
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
// The MIT License (MIT)
//
// Copyright (c) 2016 Polidea
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Foundation

class WeakBox<T: AnyObject>: CustomDebugStringConvertible {
Expand All @@ -35,3 +13,51 @@ extension WeakBox {
return "WeakBox(\(String(describing: value)))"
}
}

/// `ThreadSafeBox` is a helper class that allows use of resource (value) in a thread safe manner.
/// All read and write calls are wrapped in concurrent `DispatchQueue` which protects writing to
/// resource from more than 1 thread at a time.
class ThreadSafeBox<T>: CustomDebugStringConvertible {
private let queue = DispatchQueue(label: "com.polidea.RxBluetoothKit.ThreadSafeBox")
fileprivate var value: T
init(value: T) {
self.value = value
}

func read<Result: Any>(_ block: (T) -> Result) -> Result {
var result: Result! = nil
queue.sync {
result = block(value)
}
return result
}

func write(_ block: @escaping (inout T) -> Void) {
queue.async {
block(&self.value)
}
}

func writeSync(_ block: @escaping (inout T) -> Void) {
queue.sync {
block(&self.value)
}
}

@discardableResult func compareAndSet(compare: (T) -> Bool, set: @escaping (inout T) -> Void) -> Bool {
var result: Bool = false
queue.sync {
result = compare(value)
if result {
set(&self.value)
}
}
return result
}
}

extension ThreadSafeBox {
var debugDescription: String {
return "ThreadSafeBox(\(String(describing: value)))"
}
}
9 changes: 9 additions & 0 deletions iOS/RxBluetoothKit/CBCentral+Uuid.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Foundation
import CoreBluetooth

extension CBCentral {
/// There is still no identifier property for macOS, that's why we need to retrieve it by value method
var uuidIdentifier: UUID {
return value(forKey: "identifier") as! NSUUID as UUID
}
}
Loading

0 comments on commit 839649c

Please sign in to comment.