diff --git a/Bond.xcodeproj/project.pbxproj b/Bond.xcodeproj/project.pbxproj index 88969210..a5cfd91e 100644 --- a/Bond.xcodeproj/project.pbxproj +++ b/Bond.xcodeproj/project.pbxproj @@ -321,6 +321,15 @@ ECFF44BC21692B5800B5EDB0 /* OutlineChangesetConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFF44BB21692B5800B5EDB0 /* OutlineChangesetConvertible.swift */; }; ECFF44BD21692B5800B5EDB0 /* OutlineChangesetConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFF44BB21692B5800B5EDB0 /* OutlineChangesetConvertible.swift */; }; ECFF44BE21692B5800B5EDB0 /* OutlineChangesetConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFF44BB21692B5800B5EDB0 /* OutlineChangesetConvertible.swift */; }; + FCDA454F2229C25A001E0C3D /* CBCentralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA454E2229C25A001E0C3D /* CBCentralManager.swift */; }; + FCDA45502229C25A001E0C3D /* CBCentralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA454E2229C25A001E0C3D /* CBCentralManager.swift */; }; + FCDA45512229C25A001E0C3D /* CBCentralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA454E2229C25A001E0C3D /* CBCentralManager.swift */; }; + FCDA45542229C2DE001E0C3D /* CBPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA45532229C2DE001E0C3D /* CBPeripheral.swift */; }; + FCDA45552229C2DE001E0C3D /* CBPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA45532229C2DE001E0C3D /* CBPeripheral.swift */; }; + FCDA45562229C2DE001E0C3D /* CBPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA45532229C2DE001E0C3D /* CBPeripheral.swift */; }; + FCDA45592229C322001E0C3D /* CBPeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA45582229C322001E0C3D /* CBPeripheralManager.swift */; }; + FCDA455A2229C322001E0C3D /* CBPeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA45582229C322001E0C3D /* CBPeripheralManager.swift */; }; + FCDA455B2229C322001E0C3D /* CBPeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCDA45582229C322001E0C3D /* CBPeripheralManager.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -538,6 +547,9 @@ ECFF44B02168C21F00B5EDB0 /* Array2D.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Array2D.swift; sourceTree = ""; }; ECFF44B62168F5C000B5EDB0 /* Instantiatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instantiatable.swift; sourceTree = ""; }; ECFF44BB21692B5800B5EDB0 /* OutlineChangesetConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutlineChangesetConvertible.swift; sourceTree = ""; }; + FCDA454E2229C25A001E0C3D /* CBCentralManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBCentralManager.swift; sourceTree = ""; }; + FCDA45532229C2DE001E0C3D /* CBPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBPeripheral.swift; sourceTree = ""; }; + FCDA45582229C322001E0C3D /* CBPeripheralManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBPeripheralManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -708,6 +720,7 @@ ECFF44B62168F5C000B5EDB0 /* Instantiatable.swift */, 90C04D3E1E8F0B1D000077C8 /* Signal+Heartbeat.swift */, EC0220091F8BE898002F8380 /* Property+BidirectionalMap.swift */, + FCDA45492229C22B001E0C3D /* CoreBluetooth */, 90C04D571E8F0B4A000077C8 /* UIKit */, 90C04D581E8F0B5E000077C8 /* AppKit */, 90C04D591E8F0B75000077C8 /* Shared */, @@ -882,6 +895,16 @@ path = "Data Sources"; sourceTree = ""; }; + FCDA45492229C22B001E0C3D /* CoreBluetooth */ = { + isa = PBXGroup; + children = ( + FCDA454E2229C25A001E0C3D /* CBCentralManager.swift */, + FCDA45532229C2DE001E0C3D /* CBPeripheral.swift */, + FCDA45582229C322001E0C3D /* CBPeripheralManager.swift */, + ); + path = CoreBluetooth; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1154,6 +1177,7 @@ 90A4437A1E91390000D611FE /* BNDProtocolProxyBase.m in Sources */, 90A4430F1E9055D100D611FE /* NSSlider.swift in Sources */, 90C04DAF1E8F0B97000077C8 /* UIApplication.swift in Sources */, + FCDA454F2229C25A001E0C3D /* CBCentralManager.swift in Sources */, EC930F46222B1EA7000D397C /* TreeView.swift in Sources */, EC0D8D2B21C69BC6003F8EF2 /* UITableView+DataSource.swift in Sources */, 90C04D721E8F0B89000077C8 /* DynamicSubject.swift in Sources */, @@ -1167,6 +1191,7 @@ 90A443141E9055D200D611FE /* NSView.swift in Sources */, 90C04DB41E8F0B97000077C8 /* UIControl.swift in Sources */, 90C04DC01E8F0B97000077C8 /* UISwitch.swift in Sources */, + FCDA45542229C2DE001E0C3D /* CBPeripheral.swift in Sources */, EC930F41222B105F000D397C /* TreeProtocol+Differ.swift in Sources */, EC1F12712167BFB4002F0D1B /* UnorderedCollectionDiff.swift in Sources */, 90C04D731E8F0B89000077C8 /* Observable.swift in Sources */, @@ -1219,6 +1244,7 @@ ECAFB8D9200B60AA00EE0669 /* ProtocolProxyController.swift in Sources */, 90C04DB71E8F0B97000077C8 /* UIImageView.swift in Sources */, EC1F12792167C0B7002F0D1B /* UnorderedCollectionOperation.swift in Sources */, + FCDA45592229C322001E0C3D /* CBPeripheralManager.swift in Sources */, ECBCE0DC2161712C0078E03B /* UnorderedCollectionChangeset.swift in Sources */, ECBCE0C921616A710078E03B /* OrderedCollectionChangeset.swift in Sources */, 90C04DBF1E8F0B97000077C8 /* UIStepper.swift in Sources */, @@ -1282,6 +1308,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FCDA455A2229C322001E0C3D /* CBPeripheralManager.swift in Sources */, 90A4437D1E91391B00D611FE /* BNDProtocolProxyBase.m in Sources */, ECC1A6D1207A18DC00AE762C /* NSTableView+DataSource.swift in Sources */, 90A443361E9055D800D611FE /* UISwitch.swift in Sources */, @@ -1324,6 +1351,7 @@ ECBC51BF2161637A00BE80EC /* OrderedCollectionOperation+Strideable+Undo.swift in Sources */, ECBC51BA2161637A00BE80EC /* ChangesetContainer.swift in Sources */, ECBCE0C72161696B0078E03B /* OrderedCollectionOperation.swift in Sources */, + FCDA45552229C2DE001E0C3D /* CBPeripheral.swift in Sources */, 90C04D871E8F0B8D000077C8 /* NSTableView.swift in Sources */, 90C04D691E8F0B89000077C8 /* ProtocolProxy.swift in Sources */, 90A4432D1E9055D800D611FE /* UIImageView.swift in Sources */, @@ -1359,6 +1387,7 @@ ECAFB8D5200A7E3900EE0669 /* BNDInvocation.swift in Sources */, ECBC51C22161637A00BE80EC /* OrderedCollectionDiff+IndexPath.swift in Sources */, 90C04D831E8F0B8D000077C8 /* NSProgressIndicator.swift in Sources */, + FCDA45502229C25A001E0C3D /* CBCentralManager.swift in Sources */, 90C04D6C1E8F0B89000077C8 /* NotificationCenter.swift in Sources */, 90A443251E9055D800D611FE /* UIApplication.swift in Sources */, EC0D8D3021C69C3C003F8EF2 /* UICollectionView+DataSource.swift in Sources */, @@ -1382,6 +1411,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + FCDA455B2229C322001E0C3D /* CBPeripheralManager.swift in Sources */, 90A4437E1E91391D00D611FE /* BNDProtocolProxyBase.m in Sources */, ECC1A6D2207A18DC00AE762C /* NSTableView+DataSource.swift in Sources */, 90A4431E1E9055D200D611FE /* NSSlider.swift in Sources */, @@ -1424,6 +1454,7 @@ ECBC51D32161637B00BE80EC /* OrderedCollectionDiff+Strideable+Patch.swift in Sources */, ECBC51D42161637B00BE80EC /* OrderedCollectionOperation+Strideable+Undo.swift in Sources */, ECBCE0C82161696B0078E03B /* OrderedCollectionOperation.swift in Sources */, + FCDA45562229C2DE001E0C3D /* CBPeripheral.swift in Sources */, ECBC51CF2161637B00BE80EC /* ChangesetContainer.swift in Sources */, 90C04DA21E8F0B96000077C8 /* UINavigationBar.swift in Sources */, 9025DCB01F981694007B7689 /* Property+BidirectionalMap.swift in Sources */, @@ -1459,6 +1490,7 @@ 90A4431D1E9055D200D611FE /* NSSegmentedControl.swift in Sources */, ECBC51D72161637B00BE80EC /* OrderedCollectionDiff+IndexPath.swift in Sources */, 90A443191E9055D200D611FE /* NSImageView.swift in Sources */, + FCDA45512229C25A001E0C3D /* CBCentralManager.swift in Sources */, 90A4431B1E9055D200D611FE /* NSPopUpButton.swift in Sources */, 90A443211E9055D200D611FE /* NSTextField.swift in Sources */, EC0D8D3121C69C3C003F8EF2 /* UICollectionView+DataSource.swift in Sources */, diff --git a/Sources/Bond/CoreBluetooth/CBCentralManager.swift b/Sources/Bond/CoreBluetooth/CBCentralManager.swift new file mode 100644 index 00000000..54efc14d --- /dev/null +++ b/Sources/Bond/CoreBluetooth/CBCentralManager.swift @@ -0,0 +1,90 @@ +// +// CBCentralManager.swift +// Bond +// +// Created by Pavlo Naumenko on 3/1/19. +// Copyright © 2019 Swift Bond. All rights reserved. +// + +import CoreBluetooth +import ReactiveKit + +#if os(iOS) || os(tvOS) + +public extension ReactiveExtensions where Base: CBCentralManager { + + /// A `ProtocolProxy` for the central manager delegate. + /// + /// - Note: Accessing this property for the first time will replace central manager's current delegate + /// with a protocol proxy object (an object that is stored in this property). + /// Current delegate will be used as `forwardTo` delegate of protocol proxy. + public var delegate: ProtocolProxy { + return protocolProxy(for: CBCentralManagerDelegate.self, keyPath: \.delegate) + } + + /// A signal that emits central manager state. + /// + /// - Note: Uses central manager's `delegate` protocol proxy to observe calls made to `CBCentralManagerDelegate.centralManagerDidUpdateState(_:)` method. + @available(iOS 10.0, *) + public var didUpdateState: SafeSignal { + return delegate.signal(for: #selector(CBCentralManagerDelegate.centralManagerDidUpdateState(_:))) { (subject: SafePublishSubject, central: CBCentralManager) in + subject.next(central.state) + } + } + + /// A signal that emits a dictionary containing information about central that was preserved by the system at the time the app was terminated. + /// + /// - Note: Uses central manager's `delegate` protocol proxy to observe calls made to `CBCentralManagerDelegate.centralManager(_:willRestoreState:)` method. + @available(iOS 5.0, *) + public var willRestoreState: SafeSignal<[String : Any]> { + return delegate.signal(for: #selector(CBCentralManagerDelegate.centralManager(_:willRestoreState:))) { (subject: SafePublishSubject<[String : Any]>, central: CBCentralManager, dict: [String : Any]) in + subject.next(dict) + } + } + + /// A signal that emits a CBPeripheral object with a dictionary containing any advertisement and scan response data and also current RSSI of peripheral, in dBm. + /// + /// - Note: Uses central manager's `delegate` protocol proxy to observe calls made to `CBCentralManagerDelegate.centralManager(_:didDiscover:advertisementData:rssi:)` method. + @available(iOS 5.0, *) + public var didDiscoverPeripheral: SafeSignal<(CBPeripheral, [String : Any], NSNumber)> { + return delegate.signal(for: #selector(CBCentralManagerDelegate.centralManager(_:didDiscover:advertisementData:rssi:))) { (subject: SafePublishSubject<(CBPeripheral, [String : Any], NSNumber)>, + central: CBCentralManager, + peripheral: CBPeripheral, + advertisementData: [String : Any], + RSSI: NSNumber) in + subject.next((peripheral, advertisementData, RSSI)) + } + } + + /// A signal that emits the CBPeripheral that has connected. + /// + /// - Note: Uses central manager's `delegate` protocol proxy to observe calls made to `CBCentralManagerDelegate.centralManager(_:didConnect:)` method. + @available(iOS 5.0, *) + public var didConnectPeripheral: SafeSignal { + return delegate.signal(for: #selector(CBCentralManagerDelegate.centralManager(_:didConnect:))) { (subject: SafePublishSubject, central: CBCentralManager, peripheral: CBPeripheral) in + subject.next(peripheral) + } + } + + /// A signal that emits the CBPeripheral that has failed to connect and the cause of the failure. + /// + /// - Note: Uses central manager's `delegate` protocol proxy to observe calls made to `CBCentralManagerDelegate.centralManager(_:didFailToConnect:error:)` method. + @available(iOS 5.0, *) + public var didFailToConnectPeripheral: SafeSignal<(CBPeripheral, Error?)> { + return delegate.signal(for: #selector(CBCentralManagerDelegate.centralManager(_:didFailToConnect:error:))) { (subject: SafePublishSubject<(CBPeripheral, Error?)>, central: CBCentralManager, peripheral: CBPeripheral, error: Error?) in + subject.next((peripheral, error)) + } + } + + /// A signal that emits the CBPeripheral that has disconnected and if an error occurred, the cause of the failure. + /// + /// - Note: Uses central manager's `delegate` protocol proxy to observe calls made to `CBCentralManagerDelegate.centralManager(_:didDisconnectPeripheral:error:)` method. + @available(iOS 5.0, *) + public var didDisconnectPeripheral: SafeSignal<(CBPeripheral, Error?)> { + return delegate.signal(for: #selector(CBCentralManagerDelegate.centralManager(_:didDisconnectPeripheral:error:))) { (subject: SafePublishSubject<(CBPeripheral, Error?)>, central: CBCentralManager, peripheral: CBPeripheral, error: Error?) in + subject.next((peripheral, error)) + } + } +} + +#endif diff --git a/Sources/Bond/CoreBluetooth/CBPeripheral.swift b/Sources/Bond/CoreBluetooth/CBPeripheral.swift new file mode 100644 index 00000000..31fe5ee5 --- /dev/null +++ b/Sources/Bond/CoreBluetooth/CBPeripheral.swift @@ -0,0 +1,166 @@ +// +// CBPeripheral.swift +// Bond +// +// Created by Pavlo Naumenko on 3/1/19. +// Copyright © 2019 Swift Bond. All rights reserved. +// + +import CoreBluetooth +import ReactiveKit + +#if os(iOS) || os(tvOS) + +public extension ReactiveExtensions where Base: CBPeripheral { + + /// A `ProtocolProxy` for the peripheral delegate. + /// + /// - Note: Accessing this property for the first time will replace peripheral's current delegate + /// with a protocol proxy object (an object that is stored in this property). + /// Current delegate will be used as `forwardTo` delegate of protocol proxy. + public var delegate: ProtocolProxy { + return protocolProxy(for: CBPeripheralDelegate.self, keyPath: \.delegate) + } + + /// A signal that emits peripheral's name update. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheralDidUpdateName(_:)` method. + @available(iOS 6.0, *) + public var didUpdateName: SafeSignal { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheralDidUpdateName(_:))) { (subject: SafePublishSubject, peripheral: CBPeripheral) in + subject.next(peripheral.name) + } + } + + /// A signal that emits the peripheral providing this update and The services that have been invalidated. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didModifyServices:)` method. + @available(iOS 7.0, *) + public var didModifyServices: SafeSignal<[CBService]> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didModifyServices:))) { (subject: SafePublishSubject<[CBService]>, peripheral: CBPeripheral, invalidatedServices: [CBService]) in + subject.next(invalidatedServices) + } + } + + /// A signal that emits the current RSSI of the link and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didReadRSSI:error:)` method. + @available(iOS 8.0, *) + public var didReadRSSI: SafeSignal<(NSNumber, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didReadRSSI:error:))) { (subject: SafePublishSubject<(NSNumber, Error?)>, peripheral: CBPeripheral, RSSI: NSNumber, error: Error?) in + subject.next((RSSI, error)) + } + } + + /// A signal that emits peripheral's discovered services and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didDiscoverServices:)` method. + @available(iOS 5.0, *) + public var didDiscoverServices: SafeSignal<([CBService]?, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didDiscoverServices:))) { (subject: SafePublishSubject<([CBService]?, Error?)>, peripheral: CBPeripheral, error: Error?) in + subject.next((peripheral.services, error)) + } + } + + /// A signal that emits CBService object containing the included services and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didDiscoverIncludedServicesFor:error:)` method. + @available(iOS 5.0, *) + public var didDiscoverIncludedServicesForService: SafeSignal<(CBService, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didDiscoverIncludedServicesFor:error:))) { (subject: SafePublishSubject<(CBService, Error?)>, peripheral: CBPeripheral, service: CBService, error: Error?) in + subject.next((service, error)) + } + } + + /// A signal that emits CBService object containing the characteristic(s) and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didDiscoverCharacteristicsFor:error:)` method. + @available(iOS 5.0, *) + public var didDiscoverCharacteristicsForService: SafeSignal<(CBService, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didDiscoverCharacteristicsFor:error:))) { (subject: SafePublishSubject<(CBService, Error?)>, peripheral: CBPeripheral, service: CBService, error: Error?) in + subject.next((service, error)) + } + } + + /// A signal that emits CBCharacteristic object and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:)` method. + @available(iOS 5.0, *) + public var didUpdateValueForCharacteristic: SafeSignal<(CBCharacteristic, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:) as ((CBPeripheralDelegate) -> (CBPeripheral, CBCharacteristic, Error?) -> Void)?)) { (subject: SafePublishSubject<(CBCharacteristic, Error?)>, peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) in + subject.next((characteristic, error)) + } + } + + /// A signal that emits CBCharacteristic object and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didWriteValueFor:error:)` method. + @available(iOS 5.0, *) + public var didWriteValueForCharacteristic: SafeSignal<(CBCharacteristic, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didWriteValueFor:error:) as ((CBPeripheralDelegate) -> (CBPeripheral, CBCharacteristic, Error?) -> Void)?)) { (subject: SafePublishSubject<(CBCharacteristic, Error?)>, peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) in + subject.next((characteristic, error)) + } + } + + /// A signal that emits CBCharacteristic object and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didUpdateNotificationStateFor:error:)` method. + @available(iOS 5.0, *) + public var didUpdateNotificationStateForCharacteristics: SafeSignal<(CBCharacteristic, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didUpdateNotificationStateFor:error:))) { (subject: SafePublishSubject<(CBCharacteristic, Error?)>, peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) in + subject.next((characteristic, error)) + } + } + + /// A signal that emits CBCharacteristic object and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didDiscoverDescriptorsFor:error:)` method. + @available(iOS 5.0, *) + public var didDiscoverDescriptorsForCharacteristics: SafeSignal<(CBCharacteristic, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didDiscoverDescriptorsFor:error:))) { (subject: SafePublishSubject<(CBCharacteristic, Error?)>, peripheral: CBPeripheral, characteristic: CBCharacteristic, error: Error?) in + subject.next((characteristic, error)) + } + } + + /// A signal that emits CBDescriptor object and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:)` method. + @available(iOS 5.0, *) + public var didUpdateValueForDescriptor: SafeSignal<(CBDescriptor, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didUpdateValueFor:error:) as ((CBPeripheralDelegate) -> (CBPeripheral, CBDescriptor, Error?) -> Void)?)) { (subject: SafePublishSubject<(CBDescriptor, Error?)>, peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) in + subject.next((descriptor, error)) + } + } + + /// A signal that emits CBDescriptor object and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didWriteValueFor:error:)` method. + @available(iOS 5.0, *) + public var didWriteValueForDescriptor: SafeSignal<(CBDescriptor, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didWriteValueFor:error:) as ((CBPeripheralDelegate) -> (CBPeripheral, CBDescriptor, Error?) -> Void)?)) { (subject: SafePublishSubject<(CBDescriptor, Error?)>, peripheral: CBPeripheral, descriptor: CBDescriptor, error: Error?) in + subject.next((descriptor, error)) + } + } + + /// A signal that emits void to notify when peripheral is again ready to send characteristic value updates. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheralIsReady(toSendWriteWithoutResponse:)` method. + @available(iOS 5.0, *) + public var isReadyToSendWriteWithoutResponse: SafeSignal { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheralIsReady(toSendWriteWithoutResponse:))) { (subject: SafePublishSubject, peripheral: CBPeripheral) in + subject.next() + } + } + + /// A signal that emits CBL2CAPChannel object and if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral's `delegate` protocol proxy to observe calls made to `CBPeripheralDelegate.peripheral(_:didOpen:error:)` method. + @available(iOS 11.0, *) + public var didOpenChannel: SafeSignal<(CBL2CAPChannel?, Error?)> { + return delegate.signal(for: #selector(CBPeripheralDelegate.peripheral(_:didOpen:error:))) { (subject: SafePublishSubject<(CBL2CAPChannel?, Error?)>, channel: CBL2CAPChannel?, error: Error?) in + subject.next((channel, error)) + } + } +} + +#endif diff --git a/Sources/Bond/CoreBluetooth/CBPeripheralManager.swift b/Sources/Bond/CoreBluetooth/CBPeripheralManager.swift new file mode 100644 index 00000000..6d29e8d1 --- /dev/null +++ b/Sources/Bond/CoreBluetooth/CBPeripheralManager.swift @@ -0,0 +1,145 @@ +// +// CBPeripheralManager.swift +// Bond +// +// Created by Pavlo Naumenko on 3/1/19. +// Copyright © 2019 Swift Bond. All rights reserved. +// + +import CoreBluetooth +import ReactiveKit + +#if os(iOS) || os(tvOS) + +public extension ReactiveExtensions where Base: CBPeripheralManager { + /// A `ProtocolProxy` for the peripheral manager delegate. + /// + /// - Note: Accessing this property for the first time will replace peripheral manager's current delegate + /// with a protocol proxy object (an object that is stored in this property). + /// Current delegate will be used as `forwardTo` delegate of protocol proxy. + public var delegate: ProtocolProxy { + return protocolProxy(for: CBPeripheralManagerDelegate.self, keyPath: \.delegate) + } + + /// A signal that emits peripheral manager state. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManagerDidUpdateState(_:)` method. + @available(iOS 10.0, *) + public var didUpdateState: SafeSignal { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManagerDidUpdateState(_:))) { (subject: SafePublishSubject, peripheral: CBPeripheralManager) in + subject.next(peripheral.state) + } + } + + /// A signal that emits a dictionary containing information about peripheral that was preserved by the system at the time the app was terminated. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:willRestoreState:)` method. + @available(iOS 6.0, *) + public var willRestoreState: SafeSignal<[String : Any]> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:willRestoreState:))) { (subject: SafePublishSubject<[String : Any]>, peripheral: CBPeripheralManager, dict: [String : Any]) in + subject.next(dict) + } + } + + /// A signal that emits error if any occurred, the cause of the failure. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManagerDidStartAdvertising(_:error:)` method. + @available(iOS 6.0, *) + public var didStartAdvertising: SafeSignal { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManagerDidStartAdvertising(_:error:))) { (subject: SafePublishSubject, peripheral: CBPeripheralManager, error: Error?) in + subject.next(error) + } + } + + /// A signal that emits the service that was added to the local database if an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:didAdd:error:)` method. + @available(iOS 6.0, *) + public var didAddService: SafeSignal<(CBService, Error?)> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:didAdd:error:))) { (subject: SafePublishSubject<(CBService, Error?)>, central: CBCentralManager, service: CBService, error: Error?) in + subject.next((service, error)) + } + } + + /// A signal that emits the central that issued the command and the characteristic on which notifications or indications were enabled. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:central:didSubscribeTo:)` method. + @available(iOS 6.0, *) + public var didSubscribeToCharacteristic: SafeSignal<(CBCentral, CBCharacteristic)> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:central:didSubscribeTo:))) { (subject: SafePublishSubject<(CBCentral, CBCharacteristic)>, peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) in + subject.next((central, characteristic)) + } + } + + /// A signal that emits the central that issued the command and the characteristic on which notifications or indications were disabled. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:central:didSubscribeTo:)` method. + @available(iOS 6.0, *) + public var didUnsubscribeFromCharacteristic: SafeSignal<(CBCentral, CBCharacteristic)> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:central:didUnsubscribeFrom:))) { (subject: SafePublishSubject<(CBCentral, CBCharacteristic)>, peripheral: CBPeripheralManager, central: CBCentral, characteristic: CBCharacteristic) in + subject.next((central, characteristic)) + } + } + + /// A signal that emits a CBATTRequest object. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:didReceiveRead:)` method. + @available(iOS 6.0, *) + public var didReceiveReadRequest: SafeSignal { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:didReceiveRead:))) { (subject: SafePublishSubject, peripheral: CBPeripheralManager, request: CBATTRequest) in + subject.next(request) + } + } + + /// A signal that emits a list of one or more CBATTRequest objects. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:didReceiveWrite:)` method. + @available(iOS 6.0, *) + public var didReceiveWriteRequests: SafeSignal<[CBATTRequest]> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:didReceiveWrite:))) { (subject: SafePublishSubject<[CBATTRequest]>, peripheral: CBPeripheralManager, requests: [CBATTRequest]) in + subject.next(requests) + } + } + + /// A signal that emits Void after a failed call to updateValue:forCharacteristic:onSubscribedCentrals: when peripheral is again ready to send characteristic value updates. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManagerIsReady(toUpdateSubscribers:)` method. + @available(iOS 6.0, *) + public var IsReadyToUpdateSubscribers: SafeSignal { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManagerIsReady(toUpdateSubscribers:))) { (subject: SafePublishSubject, peripheral: CBPeripheralManager) in + subject.next() + } + } + + /// A signal that emits the PSM of the channel that was published. If an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:didPublishL2CAPChannel:error:)` method. + @available(iOS 6.0, *) + public var didPublishL2CAPChannel: SafeSignal<(CBL2CAPPSM, Error?)> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:didPublishL2CAPChannel:error:))) { (subject: SafePublishSubject<(CBL2CAPPSM, Error?)>, peripheral: CBPeripheralManager, PSM: CBL2CAPPSM, error: Error?) in + subject.next((PSM, error)) + } + } + + /// A signal that emits the PSM of the channel that was unpublished. If an error occurred, the cause of the failure. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:didUnpublishL2CAPChannel:error:)` method. + @available(iOS 6.0, *) + public var didUnpublishL2CAPChannel: SafeSignal<(CBL2CAPPSM, Error?)> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:didUnpublishL2CAPChannel:error:))) { (subject: SafePublishSubject<(CBL2CAPPSM, Error?)>, peripheral: CBPeripheralManager, PSM: CBL2CAPPSM, error: Error?) in + subject.next((PSM, error)) + } + } + + /// A signal that emits CBL2CAPChannel when peripheral receives an ATT request or command for one or more characteristics with a dynamic value. + /// + /// - Note: Uses peripheral manager's `delegate` protocol proxy to observe calls made to `CBPeripheralManagerDelegate.peripheralManager(_:didOpen:error:)` method. + @available(iOS 11.0, *) + public var didOpenChannel: SafeSignal<(CBL2CAPChannel?, Error?)> { + return delegate.signal(for: #selector(CBPeripheralManagerDelegate.peripheralManager(_:didOpen:error:))) { (subject: SafePublishSubject<(CBL2CAPChannel?, Error?)>, peripheral: CBPeripheralManager, channel: CBL2CAPChannel?, error: Error?) in + subject.next((channel, error)) + } + } +} + +#endif