From 0905f191fbb81745d8b066e59195af5993f21ad1 Mon Sep 17 00:00:00 2001 From: Nick Kibysh Date: Wed, 13 Sep 2023 11:17:29 +0200 Subject: [PATCH] delete tage --- Sources/iOS-BLE-Library-Mock/Alias.swift | 96 ------ .../CentralManager/CentralManager.swift | 264 -------------- .../CentralManager/Model/ScanResult.swift | 25 -- .../ReactiveCentralManagerDelegate.swift | 91 ----- .../Peripheral/Peripheral+Writer.swift | 226 ------------ .../Peripheral/Peripheral.swift | 283 --------------- .../ReactivePeripheralDelegate.swift | 173 ---------- .../Utilities/AdvertisementData.swift | 113 ------ .../Utilities/AsyncCharacteristicData.swift | 34 -- .../Utilities/CBManagerState.swift | 37 -- .../Extensions/CBManagerState+Ext.swift | 22 -- .../Utilities/Extensions/Data+Ext.swift | 44 --- .../Extensions/Publishers+Async.swift | 325 ------------------ .../Utilities/Logger.swift | 58 ---- .../Publishers/ContinuationSubscriber.swift | 88 ----- .../Publishers/Publishers+Bluetooth.swift | 39 --- .../Publishers/Publishers+Connectable.swift | 58 ---- .../Publishers+FailablePrefix.swift.swift | 128 ------- .../Publishers/Publishers+GuestList.swift | 121 ------- .../Publishers/Publishers+Peripheral.swift | 71 ---- .../iOS-BLE-Library-Mock/Utilities/RSSI.swift | 64 ---- .../Utilities/UnimplementedError.swift | 12 - 22 files changed, 2372 deletions(-) delete mode 100644 Sources/iOS-BLE-Library-Mock/Alias.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/CentralManager/CentralManager.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/CentralManager/Model/ScanResult.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/CentralManager/ReactiveCentralManagerDelegate.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral+Writer.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Peripheral/ReactivePeripheralDelegate.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/AdvertisementData.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/AsyncCharacteristicData.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/CBManagerState.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Extensions/CBManagerState+Ext.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Data+Ext.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Publishers+Async.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Logger.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Publishers/ContinuationSubscriber.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Bluetooth.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Connectable.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+FailablePrefix.swift.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+GuestList.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Peripheral.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/RSSI.swift delete mode 100644 Sources/iOS-BLE-Library-Mock/Utilities/UnimplementedError.swift diff --git a/Sources/iOS-BLE-Library-Mock/Alias.swift b/Sources/iOS-BLE-Library-Mock/Alias.swift deleted file mode 100644 index 3f8811d..0000000 --- a/Sources/iOS-BLE-Library-Mock/Alias.swift +++ /dev/null @@ -1,96 +0,0 @@ -/* -* Copyright (c) 2020, Nordic Semiconductor -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, -* are permitted provided that the following conditions are met: -* -* 1. Redistributions of source code must retain the above copyright notice, this -* list of conditions and the following disclaimer. -* -* 2. Redistributions in binary form must reproduce the above copyright notice, this -* list of conditions and the following disclaimer in the documentation and/or -* other materials provided with the distribution. -* -* 3. Neither the name of the copyright holder nor the names of its contributors may -* be used to endorse or promote products derived from this software without -* specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -* POSSIBILITY OF SUCH DAMAGE. -*/ - -import CoreBluetoothMock - -// Copy this file to your project to start using CoreBluetoothMock classes -// without having to refactor any of your code. You will just have to remove -// the imports to CoreBluetooth to fix conflicts and initiate the manager -// using CBCentralManagerFactory, instad of just creating a CBCentralManager. - -// disabled for Xcode 12.5 beta -//typealias CBPeer = CBMPeer -//typealias CBAttribute = CBMAttribute -public typealias CBCentralManagerFactory = CBMCentralManagerFactory -public typealias CBUUID = CBMUUID -public typealias CBError = CBMError -public typealias CBATTError = CBMATTError -public typealias CBManagerState = CBMManagerState -public typealias CBPeripheralState = CBMPeripheralState -public typealias CBCentralManager = CBMCentralManager -public typealias CBCentralManagerDelegate = CBMCentralManagerDelegate -public typealias CBPeripheral = CBMPeripheral -public typealias CBPeripheralDelegate = CBMPeripheralDelegate -public typealias CBService = CBMService -public typealias CBCharacteristic = CBMCharacteristic -public typealias CBCharacteristicWriteType = CBMCharacteristicWriteType -public typealias CBCharacteristicProperties = CBMCharacteristicProperties -public typealias CBDescriptor = CBMDescriptor -public typealias CBConnectionEvent = CBMConnectionEvent -public typealias CBConnectionEventMatchingOption = CBMConnectionEventMatchingOption -@available(iOS 11.0, tvOS 11.0, watchOS 4.0, *) -public typealias CBL2CAPPSM = CBML2CAPPSM -@available(iOS 11.0, tvOS 11.0, watchOS 4.0, *) -public typealias CBL2CAPChannel = CBML2CAPChannel - -public let CBCentralManagerScanOptionAllowDuplicatesKey = - CBMCentralManagerScanOptionAllowDuplicatesKey -public let CBCentralManagerOptionShowPowerAlertKey = CBMCentralManagerOptionShowPowerAlertKey -public let CBCentralManagerOptionRestoreIdentifierKey = CBMCentralManagerOptionRestoreIdentifierKey -public let CBCentralManagerScanOptionSolicitedServiceUUIDsKey = - CBMCentralManagerScanOptionSolicitedServiceUUIDsKey -public let CBConnectPeripheralOptionStartDelayKey = CBMConnectPeripheralOptionStartDelayKey -#if !os(macOS) - @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) - public let CBConnectPeripheralOptionRequiresANCS = CBMConnectPeripheralOptionRequiresANCS -#endif -public let CBCentralManagerRestoredStatePeripheralsKey = - CBMCentralManagerRestoredStatePeripheralsKey -public let CBCentralManagerRestoredStateScanServicesKey = - CBMCentralManagerRestoredStateScanServicesKey -public let CBCentralManagerRestoredStateScanOptionsKey = - CBMCentralManagerRestoredStateScanOptionsKey - -public let CBAdvertisementDataLocalNameKey = CBMAdvertisementDataLocalNameKey -public let CBAdvertisementDataServiceUUIDsKey = CBMAdvertisementDataServiceUUIDsKey -public let CBAdvertisementDataIsConnectable = CBMAdvertisementDataIsConnectable -public let CBAdvertisementDataTxPowerLevelKey = CBMAdvertisementDataTxPowerLevelKey -public let CBAdvertisementDataServiceDataKey = CBMAdvertisementDataServiceDataKey -public let CBAdvertisementDataManufacturerDataKey = CBMAdvertisementDataManufacturerDataKey -public let CBAdvertisementDataOverflowServiceUUIDsKey = CBMAdvertisementDataOverflowServiceUUIDsKey -public let CBAdvertisementDataSolicitedServiceUUIDsKey = - CBMAdvertisementDataSolicitedServiceUUIDsKey - -public let CBConnectPeripheralOptionNotifyOnConnectionKey = - CBMConnectPeripheralOptionNotifyOnConnectionKey -public let CBConnectPeripheralOptionNotifyOnDisconnectionKey = - CBMConnectPeripheralOptionNotifyOnDisconnectionKey -public let CBConnectPeripheralOptionNotifyOnNotificationKey = - CBMConnectPeripheralOptionNotifyOnNotificationKey diff --git a/Sources/iOS-BLE-Library-Mock/CentralManager/CentralManager.swift b/Sources/iOS-BLE-Library-Mock/CentralManager/CentralManager.swift deleted file mode 100644 index c1d5059..0000000 --- a/Sources/iOS-BLE-Library-Mock/CentralManager/CentralManager.swift +++ /dev/null @@ -1,264 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 18/04/2023. -// - -import Combine -import CoreBluetoothMock -import Foundation - -extension CentralManager { - public enum Err: Error { - case wrongManager - case badState(CBManagerState) - case unknownError - - public var localizedDescription: String { - switch self { - case .wrongManager: - return "Incorrect manager instance provided." - case .badState(let state): - return "Bad state: \(state)" - case .unknownError: - return "An unknown error occurred." - } - } - } -} - -private class Observer: NSObject { - @objc dynamic private weak var cm: CBCentralManager? - private weak var publisher: CurrentValueSubject? - private var observation: NSKeyValueObservation? - - init(cm: CBCentralManager, publisher: CurrentValueSubject) { - self.cm = cm - self.publisher = publisher - super.init() - } - - func setup() { - observation = observe( - \.cm?.isScanning, - options: [.old, .new], - changeHandler: { _, change in - - change.newValue?.flatMap { [weak self] new in - self?.publisher?.send(new) - } - } - ) - } -} - -/// A custom Central Manager class that extends the functionality of the standard CBCentralManager. -/// This class brings a reactive approach and is based on the Swift Combine framework. -public class CentralManager { - private let isScanningSubject = CurrentValueSubject(false) - private let killSwitchSubject = PassthroughSubject() - private lazy var observer = Observer(cm: centralManager, publisher: isScanningSubject) - - public let centralManager: CBCentralManager - public let centralManagerDelegate: ReactiveCentralManagerDelegate - - /// Initializes a new instance of `CentralManager`. - /// - Parameters: - /// - centralManagerDelegate: The delegate for the reactive central manager. Default is `ReactiveCentralManagerDelegate()`. - /// - queue: The queue to perform operations on. Default is the main queue. - public init( - centralManagerDelegate: ReactiveCentralManagerDelegate = - ReactiveCentralManagerDelegate(), queue: DispatchQueue = .main - ) { - self.centralManagerDelegate = centralManagerDelegate - self.centralManager = CBMCentralManagerFactory.instance( - delegate: centralManagerDelegate, queue: queue) - observer.setup() - } - - /// Initializes a new instance of `CentralManager` with an existing CBCentralManager instance. - /// - Parameter centralManager: An existing CBCentralManager instance. - /// - Throws: An error if the provided manager's delegate is not of type `ReactiveCentralManagerDelegate`. - public init(centralManager: CBCentralManager) throws { - guard - let reactiveDelegate = centralManager.delegate - as? ReactiveCentralManagerDelegate - else { - throw Err.wrongManager - } - - self.centralManager = centralManager - self.centralManagerDelegate = reactiveDelegate - - observer.setup() - } -} - -// MARK: Establishing or Canceling Connections with Peripherals -extension CentralManager { - /// Establishes a connection with the specified peripheral. - /// - Parameters: - /// - peripheral: The peripheral to connect to. - /// - options: Optional connection options. - /// - Returns: A publisher that emits the connected peripheral on successful connection. - /// The publisher does not finish until the peripheral is successfully connected. - /// If the peripheral was disconnected successfully, the publisher finishes without error. - /// If the connection was unsuccessful or disconnection returns an error (e.g., peripheral disconnected unexpectedly), - /// the publisher finishes with an error. - public func connect(_ peripheral: CBPeripheral, options: [String: Any]? = nil) - -> Publishers.BluetoothPublisher - { - let killSwitch = self.disconnectedPeripheralsChannel.tryFirst(where: { p in - if let e = p.1 { - throw e - } - return p.0.identifier == peripheral.identifier - }) - - return self.connectedPeripheralChannel - .filter { $0.0.identifier == peripheral.identifier } - .tryMap { p in - if let e = p.1 { - throw e - } - - return p.0 - } - .prefix(untilUntilOutputOrCompletion: killSwitch) - .bluetooth { - self.centralManager.connect(peripheral, options: options) - } - } - - /// Cancels the connection with the specified peripheral. - /// - Parameter peripheral: The peripheral to disconnect from. - /// - Returns: A publisher that emits the disconnected peripheral. - public func cancelPeripheralConnection(_ peripheral: CBPeripheral) -> Publishers.Peripheral - { - return self.disconnectedPeripheralsChannel - .tryFilter { r in - guard r.0.identifier == peripheral.identifier else { - return false - } - - if let e = r.1 { - throw e - } else { - return true - } - } - .map { $0.0 } - .first() - .peripheral { - self.centralManager.cancelPeripheralConnection(peripheral) - } - } -} - -// MARK: Retrieving Lists of Peripherals -extension CentralManager { - /// Returns a list of the peripherals connected to the system whose - /// services match a given set of criteria. - /// - /// The list of connected peripherals can include those that other apps - /// have connected. You need to connect these peripherals locally using - /// the `connect(_:options:)` method before using them. - /// - Parameter serviceUUIDs: A list of service UUIDs, represented by - /// `CBUUID` objects. - /// - Returns: A list of the peripherals that are currently connected - /// to the system and that contain any of the services - /// specified in the `serviceUUID` parameter. - public func retrieveConnectedPeripherals(withServices identifiers: [CBUUID]) - -> [CBPeripheral] - { - centralManager.retrieveConnectedPeripherals(withServices: identifiers) - } - - /// Returns a list of known peripherals by their identifiers. - /// - Parameter identifiers: A list of peripheral identifiers - /// (represented by `NSUUID` objects) from which - /// ``CBPeripheral`` objects can be retrieved. - /// - Returns: A list of peripherals that the central manager is able - /// to match to the provided identifiers. - public func retrievePeripherals(withIdentifiers identifiers: [UUID]) -> [CBPeripheral] { - centralManager.retrievePeripherals(withIdentifiers: identifiers) - } -} - -// MARK: Scanning or Stopping Scans of Peripherals -extension CentralManager { - /// Initiates a scan for peripherals with the specified services. - /// - Parameter services: The services to scan for. - /// - Returns: A publisher that emits scan results or errors. - public func scanForPeripherals(withServices services: [CBUUID]?) - -> Publishers.BluetoothPublisher - { - stopScan() - // TODO: Change to BluetoothPublisher - return centralManagerDelegate.stateSubject - .tryFirst { state in - guard let determined = state.ready else { return false } - - guard determined else { throw Err.badState(state) } - return true - } - .flatMap { _ in - // TODO: Check for mmemory leaks - return self.centralManagerDelegate.scanResultSubject - .setFailureType(to: Error.self) - } - .map { a in - return a - } - .prefix(untilOutputFrom: killSwitchSubject) - .mapError { [weak self] e in - self?.stopScan() - return e - } - .bluetooth { - self.centralManager.scanForPeripherals(withServices: services) - } - } - - /// Stops an ongoing scan for peripherals. - /// Calling this method finishes the publisher returned by ``scanForPeripherals(withServices:)``. - public func stopScan() { - centralManager.stopScan() - killSwitchSubject.send(()) - } -} - -// MARK: Channels -extension CentralManager { - /// A publisher that emits the state of the central manager. - public var stateChannel: AnyPublisher { - centralManagerDelegate - .stateSubject - .eraseToAnyPublisher() - } - - /// A publisher that emits the scanning state. - public var isScanningChannel: AnyPublisher { - isScanningSubject - .eraseToAnyPublisher() - } - - /// A publisher that emits scan results. - public var scanResultsChannel: AnyPublisher { - centralManagerDelegate.scanResultSubject - .eraseToAnyPublisher() - } - - /// A publisher that emits connected peripherals along with errors. - public var connectedPeripheralChannel: AnyPublisher<(CBPeripheral, Error?), Never> { - centralManagerDelegate.connectedPeripheralSubject - .eraseToAnyPublisher() - } - - /// A publisher that emits disconnected peripherals along with errors. - public var disconnectedPeripheralsChannel: AnyPublisher<(CBPeripheral, Error?), Never> { - centralManagerDelegate.disconnectedPeripheralsSubject - .eraseToAnyPublisher() - } -} diff --git a/Sources/iOS-BLE-Library-Mock/CentralManager/Model/ScanResult.swift b/Sources/iOS-BLE-Library-Mock/CentralManager/Model/ScanResult.swift deleted file mode 100644 index 16a5c97..0000000 --- a/Sources/iOS-BLE-Library-Mock/CentralManager/Model/ScanResult.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 19/04/2023. -// - -import CoreBluetoothMock -import Foundation - -public struct ScanResult { - public let peripheral: CBPeripheral - public let rssi: RSSI - public let advertisementData: AdvertisementData - - init(peripheral: CBPeripheral, rssi: NSNumber, advertisementData: [String: Any]) { - self.peripheral = peripheral - self.rssi = RSSI(integerLiteral: rssi.intValue) - self.advertisementData = AdvertisementData(advertisementData) - } - - public var name: String? { - peripheral.name ?? advertisementData.localName - } -} diff --git a/Sources/iOS-BLE-Library-Mock/CentralManager/ReactiveCentralManagerDelegate.swift b/Sources/iOS-BLE-Library-Mock/CentralManager/ReactiveCentralManagerDelegate.swift deleted file mode 100644 index 3f8f57b..0000000 --- a/Sources/iOS-BLE-Library-Mock/CentralManager/ReactiveCentralManagerDelegate.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 18/04/2023. -// - -import Combine -import CoreBluetoothMock -import Foundation - -open class ReactiveCentralManagerDelegate: NSObject, CBCentralManagerDelegate { - enum BluetoothError: Error { - case failedToConnect - } - - let stateSubject = CurrentValueSubject(.unknown) - let scanResultSubject = PassthroughSubject() - let connectedPeripheralSubject = PassthroughSubject<(CBPeripheral, Error?), Never>() - let disconnectedPeripheralsSubject = PassthroughSubject<(CBPeripheral, Error?), Never>() - let connectionEventSubject = PassthroughSubject<(CBPeripheral, CBConnectionEvent), Never>() - - // MARK: Monitoring Connections with Peripherals - open func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { - connectedPeripheralSubject.send((peripheral, nil)) - } - - open func centralManager( - _ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, - error: Error? - ) { - disconnectedPeripheralsSubject.send((peripheral, error)) - } - - open func centralManager( - _ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, - error: Error? - ) { - connectedPeripheralSubject.send((peripheral, error)) - } - - #if !os(macOS) - open func centralManager( - _ central: CBCentralManager, - connectionEventDidOccur event: CBConnectionEvent, - for peripheral: CBPeripheral - ) { - connectionEventSubject.send((peripheral, event)) - } - #endif - - // MARK: Discovering and Retrieving Peripherals - - open func centralManager( - _ central: CBCentralManager, didDiscover peripheral: CBPeripheral, - advertisementData: [String: Any], rssi RSSI: NSNumber - ) { - let scanResult = ScanResult( - peripheral: peripheral, - rssi: RSSI, - advertisementData: advertisementData - ) - scanResultSubject.send(scanResult) - } - - // MARK: Monitoring the Central Manager’s State - - open func centralManagerDidUpdateState(_ central: CBCentralManager) { - stateSubject.send(central.state) - } - - public func centralManager( - _ central: CBCentralManager, willRestoreState dict: [String: Any] - ) { - unimplementedError() - } - - // MARK: Monitoring the Central Manager’s Authorization - #if !os(macOS) - public func centralManager( - _ central: CBCentralManager, - didUpdateANCSAuthorizationFor peripheral: CBPeripheral - ) { - unimplementedError() - } - #endif - - // MARK: Instance Methods - // BETA - // func centralManager(CBCentralManager, didDisconnectPeripheral: CBPeripheral, timestamp: CFAbsoluteTime, isReconnecting: Bool, error: Error?) -} diff --git a/Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral+Writer.swift b/Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral+Writer.swift deleted file mode 100644 index 0f0b052..0000000 --- a/Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral+Writer.swift +++ /dev/null @@ -1,226 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 07/05/2023. -// - -import Combine -import CoreBluetoothMock -import Foundation - -extension Peripheral { - class OperationQueue { - let queue = Foundation.OperationQueue() - let peripheral: CBPeripheral - - init(peripheral: CBPeripheral) { - self.peripheral = peripheral - queue.maxConcurrentOperationCount = 1 - } - } - - class CharacteristicWriter: OperationQueue { - let writtenEventsPublisher: AnyPublisher<(CBCharacteristic, Error?), Never> - - init( - writtenEventsPublisher: AnyPublisher<(CBCharacteristic, Error?), Never>, - peripheral: CBPeripheral - ) { - self.writtenEventsPublisher = writtenEventsPublisher - super.init(peripheral: peripheral) - } - } - - class CharacteristicReader: OperationQueue { - let updateEventPublisher: AnyPublisher<(CBCharacteristic, Error?), Never> - - init( - updateEventPublisher: AnyPublisher<(CBCharacteristic, Error?), Never>, - peripheral: CBPeripheral - ) { - self.updateEventPublisher = updateEventPublisher - super.init(peripheral: peripheral) - } - } -} - -extension Peripheral.CharacteristicWriter { - func write(_ value: Data, to characteristic: CBCharacteristic) -> Future { - let operation = WriteCharacteristicOperation( - data: value, - writtenEventsPublisher: writtenEventsPublisher, - characteristic: characteristic, - peripheral: peripheral) - - queue.addOperation(operation) - - return operation.future - } -} - -extension Peripheral.CharacteristicReader { - func readValue(from characteristc: CBCharacteristic) -> Future { - let operation = ReadCharacteristicOperation( - updateEventPublisher: updateEventPublisher, - characteristic: characteristc, - peripheral: peripheral - ) - - queue.addOperation(operation) - - return operation.future - } -} - -private class BasicOperation: Operation { - let peripheral: CBPeripheral - var cancelable: AnyCancellable? - - private(set) var promise: ((Result) -> Void)? - - enum State: String { - case ready, executing, finished - - var keyPath: String { - "is\(rawValue.capitalized)" - } - } - - init(peripheral: CBPeripheral) { - self.peripheral = peripheral - } - - lazy private(set) var future: Future = Future { [unowned self] promise in - self.promise = promise - } - - var state: State = .ready { - willSet { - willChangeValue(forKey: state.keyPath) - willChangeValue(forKey: newValue.keyPath) - } - didSet { - didChangeValue(forKey: state.keyPath) - didChangeValue(forKey: state.keyPath) - } - } - - override var isExecuting: Bool { - state == .executing - } - - override var isFinished: Bool { - state == .finished - } - - override func cancel() { - cancelable?.cancel() - } - - override var isAsynchronous: Bool { - true - } -} - -private class WriteCharacteristicOperation: BasicOperation { - - let writtenEventsPublisher: AnyPublisher<(CBCharacteristic, Error?), Never> - let characteristic: CBCharacteristic - - let data: Data - - init( - data: Data, writtenEventsPublisher: AnyPublisher<(CBCharacteristic, Error?), Never>, - characteristic: CBCharacteristic, peripheral: CBPeripheral - ) { - self.data = data - self.writtenEventsPublisher = writtenEventsPublisher - self.characteristic = characteristic - super.init(peripheral: peripheral) - } - - override func main() { - peripheral.writeValue(data, for: characteristic, type: .withResponse) - } - - override func start() { - if isCancelled { - state = .finished - return - } - - self.cancelable = writtenEventsPublisher.share() - .filter { $0.0.uuid == self.characteristic.uuid } - .first() - .tryMap { v in - if let e = v.1 { - throw e - } else { - return v.0 - } - } - .sink { [unowned self] completion in - switch completion { - case .finished: - self.promise?(.success(())) - case .failure(let e): - self.promise?(.failure(e)) - } - self.state = .finished - } receiveValue: { _ in - - } - - state = .executing - main() - } -} - -private class ReadCharacteristicOperation: BasicOperation { - let updateEventPublisher: AnyPublisher<(CBCharacteristic, Error?), Never> - let characteristic: CBCharacteristic - - init( - updateEventPublisher: AnyPublisher<(CBCharacteristic, Error?), Never>, - characteristic: CBCharacteristic, peripheral: CBPeripheral - ) { - self.updateEventPublisher = updateEventPublisher - self.characteristic = characteristic - super.init(peripheral: peripheral) - } - - override func main() { - peripheral.readValue(for: characteristic) - } - - override func start() { - if isCancelled { - state = .finished - return - } - - self.cancelable = updateEventPublisher.share() - .filter { $0.0.uuid == self.characteristic.uuid } - .first() - .tryMap { v in - if let e = v.1 { - throw e - } else { - return v.0.value - } - } - .sink { [unowned self] completion in - if case .failure(let e) = completion { - self.promise?(.failure(e)) - } - self.state = .finished - } receiveValue: { v in - self.promise?(.success(v)) - } - - state = .executing - main() - } - -} diff --git a/Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral.swift b/Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral.swift deleted file mode 100644 index 22cda2f..0000000 --- a/Sources/iOS-BLE-Library-Mock/Peripheral/Peripheral.swift +++ /dev/null @@ -1,283 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 28/04/2023. -// - -import Combine -import CoreBluetooth -import CoreBluetoothMock -import Foundation - -private class Observer: NSObject { - func setup() {} -} - -private class NativeObserver: Observer { - @objc private var peripheral: CoreBluetooth.CBPeripheral - - private weak var publisher: CurrentValueSubject! - private var observation: NSKeyValueObservation? - - init( - peripheral: CoreBluetooth.CBPeripheral, - publisher: CurrentValueSubject - ) { - self.peripheral = peripheral - self.publisher = publisher - super.init() - } - - override func setup() { - observation = peripheral.observe(\.state, options: [.new]) { - [weak self] _, change in - #warning("queue can be not only main") - DispatchQueue.main.async { - guard let self else { return } - self.publisher.send(self.peripheral.state) - } - } - } -} - -private class MockObserver: Observer { - @objc private var peripheral: CBMPeripheralMock - - private weak var publisher: CurrentValueSubject! - private var observation: NSKeyValueObservation? - - init( - peripheral: CBMPeripheralMock, - publisher: CurrentValueSubject - ) { - self.peripheral = peripheral - self.publisher = publisher - super.init() - } - - override func setup() { - observation = peripheral.observe(\.state, options: [.new]) { - [weak self] _, change in - #warning("queue can be not only main") - DispatchQueue.main.async { - guard let self else { return } - self.publisher.send(self.peripheral.state) - } - } - } -} - -public class Peripheral { - /// I'm Errr from Omicron Persei 8 - public enum Err: Error { - case badDelegate - } - - public let peripheral: CBPeripheral - public let peripheralDelegate: ReactivePeripheralDelegate - - private let stateSubject = CurrentValueSubject(.disconnected) - private var observer: Observer! - private lazy var writer = CharacteristicWriter( - writtenEventsPublisher: self.peripheralDelegate.writtenCharacteristicValuesSubject - .eraseToAnyPublisher(), - peripheral: self.peripheral - ) - - private lazy var reader = CharacteristicReader( - updateEventPublisher: self.peripheralDelegate.updatedCharacteristicValuesSubject - .eraseToAnyPublisher(), - peripheral: peripheral - ) - - // TODO: Why don't we use default delegate? - public init(peripheral: CBPeripheral, delegate: ReactivePeripheralDelegate) { - self.peripheral = peripheral - self.peripheralDelegate = delegate - peripheral.delegate = delegate - - if let p = peripheral as? CBMPeripheralNative { - observer = NativeObserver(peripheral: p.peripheral, publisher: stateSubject) - observer.setup() - } else if let p = peripheral as? CBMPeripheralMock { - observer = MockObserver(peripheral: p, publisher: stateSubject) - observer.setup() - } - } -} - -// MARK: - Channels -extension Peripheral { - public var peripheralStateChannel: AnyPublisher { - stateSubject.eraseToAnyPublisher() - } -} - -extension Peripheral { - // TODO: Extract repeated code - public func discoverServices(serviceUUIDs: [CBUUID]?) - -> Publishers.BluetoothPublisher - { - let allServices = peripheralDelegate.discoveredServicesSubject - .tryCompactMap { result throws -> [CBService]? in - if let e = result.1 { - throw e - } else { - return result.0 - } - } - .flatMap { services -> Publishers.Sequence<[CBService], Error> in - Publishers.Sequence(sequence: services) - } - - let filtered: AnyPublisher - - if let serviceList = serviceUUIDs { - filtered = allServices.guestList(serviceList, keypath: \.uuid) - .eraseToAnyPublisher() - } else { - filtered = allServices.eraseToAnyPublisher() - } - - return filtered.bluetooth { - self.peripheral.discoverServices(serviceUUIDs) - } - } - - public func discoverCharacteristics( - _ characteristicUUIDs: [CBUUID]?, for service: CBService - ) -> Publishers.BluetoothPublisher { - let allCharacteristics = peripheralDelegate.discoveredCharacteristicsSubject - .filter { - $0.0.uuid == service.uuid - } - .tryCompactMap { result throws -> [CBCharacteristic]? in - if let e = result.2 { - throw e - } else { - return result.1 - } - } - .flatMap { - characteristics -> Publishers.Sequence<[CBCharacteristic], Error> in - Publishers.Sequence(sequence: characteristics) - } - - let filtered: AnyPublisher - - if let list = characteristicUUIDs { - filtered = - allCharacteristics - .guestList(list, keypath: \.uuid) - .eraseToAnyPublisher() - } else { - filtered = allCharacteristics.eraseToAnyPublisher() - } - - return filtered.bluetooth { - self.peripheral.discoverCharacteristics(characteristicUUIDs, for: service) - } - } - - public func discoverDescriptors(for characteristic: CBCharacteristic) - -> Publishers.BluetoothPublisher - { - return peripheralDelegate.discoveredDescriptorsSubject - .filter { - $0.0.uuid == characteristic.uuid - } - .tryCompactMap { result throws -> [CBDescriptor]? in - if let e = result.2 { - throw e - } else { - return result.1 - } - } - .flatMap { descriptors -> Publishers.Sequence<[CBDescriptor], Error> in - Publishers.Sequence(sequence: descriptors) - } - .bluetooth { - self.peripheral.discoverDescriptors(for: characteristic) - } - } -} - -// MARK: - Writing Characteristic and Descriptor Values -extension Peripheral { - public func writeValueWithResponse(_ data: Data, for characteristic: CBCharacteristic) - -> Publishers.BluetoothPublisher - { - return peripheralDelegate.writtenCharacteristicValuesSubject - .first(where: { $0.0.uuid == characteristic.uuid }) - .tryMap { result in - if let e = result.1 { - throw e - } else { - return () - } - } - .bluetooth { - self.peripheral.writeValue( - data, for: characteristic, type: .withResponse) - } - } - - public func writeValueWithoutResponse(_ data: Data, for characteristic: CBCharacteristic) { - peripheral.writeValue(data, for: characteristic, type: .withoutResponse) - } - - public func writeValue(_ data: Data, for descriptor: CBDescriptor) { - fatalError() - } -} - -// MARK: - Reading Characteristic and Descriptor Values -extension Peripheral { - public func readValue(for characteristic: CBCharacteristic) -> Future { - return reader.readValue(from: characteristic) - } - - public func listenValues(for characteristic: CBCharacteristic) -> AnyPublisher - { - return peripheralDelegate.updatedCharacteristicValuesSubject - .filter { $0.0.uuid == characteristic.uuid } - .tryCompactMap { (ch, err) in - if let err { - throw err - } - - return ch.value - } - .eraseToAnyPublisher() - } - - public func readValue(for descriptor: CBDescriptor) -> Future { - fatalError() - } -} - -// MARK: - Setting Notifications for a Characteristic’s Value -extension Peripheral { - public func setNotifyValue(_ isEnabled: Bool, for characteristic: CBCharacteristic) - -> Publishers.BluetoothPublisher - { - if characteristic.isNotifying == isEnabled { - return Just(isEnabled) - .setFailureType(to: Error.self) - .bluetooth {} - } - - return peripheralDelegate.notificationStateSubject - .first { $0.0.uuid == characteristic.uuid } - .tryMap { result in - if let e = result.1 { - throw e - } - return result.0.isNotifying - } - .bluetooth { - self.peripheral.setNotifyValue(isEnabled, for: characteristic) - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Peripheral/ReactivePeripheralDelegate.swift b/Sources/iOS-BLE-Library-Mock/Peripheral/ReactivePeripheralDelegate.swift deleted file mode 100644 index c342031..0000000 --- a/Sources/iOS-BLE-Library-Mock/Peripheral/ReactivePeripheralDelegate.swift +++ /dev/null @@ -1,173 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 28/04/2023. -// - -import Combine -import CoreBluetoothMock -import Foundation - -public class ReactivePeripheralDelegate: NSObject { - let l = L(category: #file) - - // MARK: Subjects - public let discoveredServicesSubject = PassthroughSubject<([CBService]?, Error?), Never>() - public let discoveredIncludedServicesSubject = PassthroughSubject< - (CBService, [CBService]?, Error?), Never - >() - public let discoveredCharacteristicsSubject = PassthroughSubject< - (CBService, [CBCharacteristic]?, Error?), Never - >() - public let discoveredDescriptorsSubject = PassthroughSubject< - (CBCharacteristic, [CBDescriptor]?, Error?), Never - >() - - // MARK: Retrieving Characteristic and Descriptor Values - public let updatedCharacteristicValuesSubject = PassthroughSubject< - (CBCharacteristic, Error?), Never - >() - public let updatedDescriptorValuesSubject = PassthroughSubject< - (CBDescriptor, Error?), Never - >() - - public let writtenCharacteristicValuesSubject = PassthroughSubject< - (CBCharacteristic, Error?), Never - >() - public let writtenDescriptorValuesSubject = PassthroughSubject< - (CBDescriptor, Error?), Never - >() - - // MARK: Managing Notifications for a Characteristic’s Value - public let notificationStateSubject = PassthroughSubject< - (CBCharacteristic, Error?), Never - >() - - // MARK: Monitoring Changes to a Peripheral’s Name or Services - public let updateNameSubject = PassthroughSubject() -} - -extension ReactivePeripheralDelegate: CBPeripheralDelegate { - // MARK: Discovering Services - - public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { - l.i(#function) - discoveredServicesSubject.send((peripheral.services, error)) - } - - public func peripheral( - _ peripheral: CBPeripheral, didDiscoverIncludedServicesFor service: CBService, - error: Error? - ) { - l.i(#function) - discoveredIncludedServicesSubject.send((service, service.includedServices, error)) - } - - // MARK: Discovering Characteristics and their Descriptors - - public func peripheral( - _ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, - error: Error? - ) { - l.i(#function) - discoveredCharacteristicsSubject.send((service, service.characteristics, error)) - } - - public func peripheral( - _ peripheral: CBPeripheral, - didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error? - ) { - l.i(#function) - discoveredDescriptorsSubject.send( - (characteristic, characteristic.descriptors, error)) - } - - // MARK: Retrieving Characteristic and Descriptor Values - - public func peripheral( - _ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, - error: Error? - ) { - l.i(#function) - updatedCharacteristicValuesSubject.send((characteristic, error)) - } - - public func peripheral( - _ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, - error: Error? - ) { - l.i(#function) - updatedDescriptorValuesSubject.send((descriptor, error)) - } - - // MARK: Writing Characteristic and Descriptor Values - - public func peripheral( - _ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, - error: Error? - ) { - l.i(#function) - writtenCharacteristicValuesSubject.send((characteristic, error)) - } - - public func peripheral( - _ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error? - ) { - l.i(#function) - writtenDescriptorValuesSubject.send((descriptor, error)) - } - - public func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) { - l.i(#function) - fatalError() - } - - // MARK: Managing Notifications for a Characteristic’s Value - - public func peripheral( - _ peripheral: CBPeripheral, - didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error? - ) { - l.i(#function) - notificationStateSubject.send((characteristic, error)) - } - - // MARK: Retrieving a Peripheral’s RSSI Data - - public func peripheral( - _ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error? - ) { - l.i(#function) - fatalError() - } - - public func peripheralDidUpdateRSSI(_ peripheral: CBPeripheral, error: Error?) { - l.i(#function) - fatalError() - } - - // MARK: Monitoring Changes to a Peripheral’s Name or Services - - public func peripheralDidUpdateName(_ peripheral: CBPeripheral) { - l.i(#function) - updateNameSubject.send(peripheral.name) - } - - public func peripheral( - _ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService] - ) { - l.i(#function) - fatalError() - } - - // MARK: Monitoring L2CAP Channels - - public func peripheral( - _ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error? - ) { - l.i(#function) - fatalError() - } - -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/AdvertisementData.swift b/Sources/iOS-BLE-Library-Mock/Utilities/AdvertisementData.swift deleted file mode 100644 index b2df1cd..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/AdvertisementData.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// AdvertisementData.swift -// iOS-BLE-Library -// -// Created by Dinesh Harjani on 23/8/22. -// - -import CoreBluetoothMock -import Foundation - -public struct AdvertisementData: Hashable { - public static func == (lhs: AdvertisementData, rhs: AdvertisementData) -> Bool { - return lhs.localName == rhs.localName - && lhs.manufacturerData == rhs.manufacturerData - && lhs.serviceData == rhs.serviceData - && lhs.serviceUUIDs == rhs.serviceUUIDs - && lhs.overflowServiceUUIDs == rhs.overflowServiceUUIDs - && lhs.txPowerLevel == rhs.txPowerLevel - && lhs.isConnectable == rhs.isConnectable - && lhs.solicitedServiceUUIDs == rhs.solicitedServiceUUIDs - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(localName) - hasher.combine(manufacturerData) - hasher.combine(serviceData) - hasher.combine(serviceUUIDs) - hasher.combine(overflowServiceUUIDs) - hasher.combine(txPowerLevel) - hasher.combine(isConnectable) - hasher.combine(solicitedServiceUUIDs) - } - - public let rawData: [String: Any] - - // MARK: - Properties - - public var localName: String? { // CBAdvertisementDataLocalNameKey - rawData[CBAdvertisementDataLocalNameKey] as? String - } - - public var manufacturerData: Data? { // CBAdvertisementDataManufacturerDataKey - rawData[CBAdvertisementDataManufacturerDataKey] as? Data - } - - public var serviceData: [CBUUID: Data]? { // CBAdvertisementDataServiceDataKey - rawData[CBAdvertisementDataServiceDataKey] as? [CBUUID: Data] - } - - public var serviceUUIDs: [CBUUID]? { // CBAdvertisementDataServiceUUIDsKey - rawData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] - } - - public var overflowServiceUUIDs: [CBUUID]? { // CBAdvertisementDataOverflowServiceUUIDsKey - rawData[CBAdvertisementDataOverflowServiceUUIDsKey] as? [CBUUID] - } - - public var txPowerLevel: Int? { // CBAdvertisementDataTxPowerLevelKey - (rawData[CBAdvertisementDataTxPowerLevelKey] as? NSNumber)?.intValue - } - - public var isConnectable: Bool? { // CBAdvertisementDataIsConnectable - (rawData[CBAdvertisementDataIsConnectable] as? NSNumber)?.boolValue - } - - public var solicitedServiceUUIDs: [CBUUID]? { // CBAdvertisementDataSolicitedServiceUUIDsKey - rawData[CBAdvertisementDataSolicitedServiceUUIDsKey] as? [CBUUID] - } - - // MARK: - Init - - public init() { - self.init([:]) - } - - public init(_ advertisementData: [String: Any]) { - self.rawData = advertisementData - } - - // MARK: - Advertised ID (MAC Address) - - internal static let ExpectedManufacturerDataPrefix: UInt8 = 225 - - public func advertisedID() -> String? { - guard let data = manufacturerData, data.count > 4 else { return nil } - var advData = data.suffix(from: 2) // Skip 'Nordic' Manufacturer Code - guard advData.removeFirst() == Self.ExpectedManufacturerDataPrefix else { - return nil - } - return advData.hexEncodedString(separator: ":").uppercased() - } -} - -// MARK: - Debug - -#if DEBUG - extension AdvertisementData { - - public static var connectableMock: AdvertisementData { - AdvertisementData([ - CBAdvertisementDataLocalNameKey: "iPhone 13", - CBAdvertisementDataIsConnectable: true as NSNumber, - ]) - } - - public static var unconnectableMock: AdvertisementData { - AdvertisementData([ - CBAdvertisementDataLocalNameKey: "iPhone 14", - CBAdvertisementDataIsConnectable: false as NSNumber, - ]) - } - } -#endif diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/AsyncCharacteristicData.swift b/Sources/iOS-BLE-Library-Mock/Utilities/AsyncCharacteristicData.swift deleted file mode 100644 index 6a95d00..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/AsyncCharacteristicData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// AsyncCharacteristicData.swift -// iOS-BLE-Library -// -// Created by Dinesh Harjani on 23/8/22. -// - -import CoreBluetoothMock -import Foundation - -public typealias AsyncStreamValue = (characteristic: CBCharacteristic, data: Data?) - -public struct AsyncCharacteristicData: AsyncSequence, AsyncIteratorProtocol { - public typealias Element = Data? - - let serviceUUID: String - let characteristicUUID: String - let stream: AsyncThrowingStream - - public func makeAsyncIterator() -> AsyncCharacteristicData { - self - } - - mutating public func next() async throws -> Element? { - for try await newValue in stream { - guard newValue.characteristic.uuid.uuidString == characteristicUUID, - let service = newValue.characteristic.service, - service.uuid.uuidString == serviceUUID - else { continue } - return newValue.data - } - return nil - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/CBManagerState.swift b/Sources/iOS-BLE-Library-Mock/Utilities/CBManagerState.swift deleted file mode 100644 index 0f15b75..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/CBManagerState.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// CBManagerState.swift -// -// -// Created by Dinesh Harjani on 23/8/22. -// - -import CoreBluetoothMock -import Foundation - -// MARK: - CBManagerState - -@available(iOS 10.0, *) -@available(macOS 10.13, *) -extension CBManagerState: CustomDebugStringConvertible, CustomStringConvertible { - - public var debugDescription: String { - return description - } - - public var description: String { - switch self { - case .poweredOff: - return "poweredOff" - case .poweredOn: - return "poweredOn" - case .resetting: - return "resetting" - case .unauthorized: - return "unauthorized" - case .unknown: - return "unknown" - case .unsupported: - return "unsupported" - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/CBManagerState+Ext.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/CBManagerState+Ext.swift deleted file mode 100644 index adfd47f..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/CBManagerState+Ext.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 19/04/2023. -// - -import CoreBluetoothMock -import Foundation - -extension CBManagerState { - var ready: Bool? { - switch self { - case .poweredOn: - return true - case .unknown, .resetting: - return nil - case .poweredOff, .unauthorized, .unsupported: - return false - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Data+Ext.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Data+Ext.swift deleted file mode 100644 index 565f23b..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Data+Ext.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// Data.swift -// -// -// Created by Dinesh Harjani on 18/8/22. -// - -import Foundation - -// MARK: - Data Extension - -extension Data { - - // MARK: HexEncodingOptions - - public struct HexEncodingOptions: OptionSet { - - public static let upperCase = HexEncodingOptions(rawValue: 1) - public static let reverseEndianness = HexEncodingOptions(rawValue: 2) - - public let rawValue: Int - - public init(rawValue: Int) { - self.rawValue = rawValue << 0 - } - } - - // MARK: hexEncodedString - - public func hexEncodedString(options: HexEncodingOptions = [], separator: String = "") - -> String - { - let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx" - - var bytes = self - if options.contains(.reverseEndianness) { - bytes.reverse() - } - return - bytes - .map { String(format: format, $0) } - .joined(separator: separator) - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Publishers+Async.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Publishers+Async.swift deleted file mode 100644 index 530e851..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Extensions/Publishers+Async.swift +++ /dev/null @@ -1,325 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 24/03/2023. -// - -import Combine -import Foundation - -extension Publisher where Failure == Never { - - var values: AsyncPublisher { - return .init(self) - } -} - -extension Publisher { - public var value: Output { - get async throws { - try await ContinuationSubscriber.withCheckedContinuation(self) - } - } -} - -//extension Publishers.Autoconnect where Upstream == Publishers.Peripheral { -// public var value: Output { -// get async throws { -// try await ContinuationSubscriber.withCheckedContinuation(self) -// } -// } -//} - -public struct AsyncPublisher: AsyncSequence where Upstream.Failure == Never { - public typealias Element = Upstream.Output - - public struct Iterator: AsyncIteratorProtocol { - public typealias Element = Upstream.Output - fileprivate let inner: Inner - - public mutating func next() async -> Element? { - return await withTaskCancellationHandler { - [inner] in - await inner.next() - } onCancel: { - [inner] in - inner.cancel() - } - } - } - - public typealias AsyncIterator = Iterator - - private let publisher: Upstream - - public init(_ publisher: Upstream) { - self.publisher = publisher - } - - public func makeAsyncIterator() -> Iterator { - let inner = Iterator.Inner() - publisher.subscribe(inner) - return Iterator(inner: inner) - } -} - -extension AsyncPublisher.Iterator { - fileprivate final class Inner: Subscriber, Cancellable { - typealias Input = Upstream.Output - typealias Failure = Upstream.Failure - - private enum State { - case awaitingSubscription - case subscribed(Subscription) - case terminal - } - - private let lock = NSLock() - private var pending: [UnsafeContinuation] = [] - private var state = State.awaitingSubscription - private var pendingDemand = Subscribers.Demand.none - - func receive(subscription: Subscription) { - lock.lock() - guard case .awaitingSubscription = state else { - lock.unlock() - subscription.cancel() - return - } - state = .subscribed(subscription) - let pendingDemand = self.pendingDemand - self.pendingDemand = .none - lock.unlock() - if pendingDemand != .none { - subscription.request(pendingDemand) - } - } - - func receive(_ input: Input) -> Subscribers.Demand { - lock.lock() - guard case .subscribed = state else { - let pending = self.pending - lock.unlock() - pending.resumeAllWithNil() - return .none - } - precondition( - !pending.isEmpty, "Received an output without requesting demand") - let continuation = pending.removeFirst() - lock.unlock() - continuation.resume(returning: input) - return .none - } - - func receive(completion: Subscribers.Completion) { - lock.lock() - state = .terminal - let pending = self.pending - lock.unlock() - pending.resumeAllWithNil() - } - - func cancel() { - lock.lock() - let pending = self.pending - guard case .subscribed(let subscription) = state else { - state = .terminal - lock.unlock() - pending.resumeAllWithNil() - return - } - state = .terminal - lock.unlock() - subscription.cancel() - pending.resumeAllWithNil() - } - - fileprivate func next() async -> Input? { - return await withUnsafeContinuation { continuation in - lock.lock() - switch state { - case .awaitingSubscription: - pending.append(continuation) - pendingDemand += 1 - lock.unlock() - case .subscribed(let subscription): - pending.append(continuation) - lock.unlock() - subscription.request(.max(1)) - case .terminal: - lock.unlock() - continuation.resume(returning: nil) - } - } - } - } -} - -extension Publisher { - var values: AsyncThrowingPublisher { - return .init(self) - } -} - -public struct AsyncThrowingPublisher: AsyncSequence { - public typealias Element = Upstream.Output - - public struct Iterator: AsyncIteratorProtocol { - - public typealias Element = Upstream.Output - - fileprivate let inner: Inner - - public mutating func next() async throws -> Element? { - try await withTaskCancellationHandler { - [inner] in - try await inner.next() - } onCancel: { - [inner] in - inner.cancel() - } - } - } - - public typealias AsyncIterator = Iterator - - private let publisher: Upstream - - public init(_ publisher: Upstream) { - self.publisher = publisher - } - - public func makeAsyncIterator() -> Iterator { - let inner = Iterator.Inner() - publisher.subscribe(inner) - return Iterator(inner: inner) - } -} - -extension AsyncThrowingPublisher.Iterator { - - fileprivate final class Inner: Subscriber, Cancellable { - typealias Input = Upstream.Output - typealias Failure = Upstream.Failure - - private enum State { - case awaitingSubscription - case subscribed(Subscription) - case terminal(Error?) - } - - private let lock = NSLock() - private var pending: [UnsafeContinuation] = [] - private var state = State.awaitingSubscription - private var pendingDemand = Subscribers.Demand.none - - func receive(subscription: Subscription) { - lock.lock() - guard case .awaitingSubscription = state else { - lock.unlock() - subscription.cancel() - return - } - state = .subscribed(subscription) - let pendingDemand = self.pendingDemand - self.pendingDemand = .none - lock.unlock() - if pendingDemand != .none { - subscription.request(pendingDemand) - } - } - - func receive(_ input: Input) -> Subscribers.Demand { - lock.lock() - guard case .subscribed = state else { - let pending = self.pending - lock.unlock() - pending.resumeAllWithNil() - return .none - } - precondition( - !pending.isEmpty, "Received an output without requesting demand") - let continuation = pending.removeFirst() - lock.unlock() - continuation.resume(returning: input) - return .none - } - - func receive(completion: Subscribers.Completion) { - lock.lock() - switch state { - case .awaitingSubscription, .subscribed: - if let continuation = pending.first { - state = .terminal(nil) - let remaining = pending.dropFirst() - lock.unlock() - switch completion { - case .finished: - continuation.resume(returning: nil) - case .failure(let error): - continuation.resume(throwing: error) - } - remaining.resumeAllWithNil() - } else if case .failure(let e) = completion { - state = .terminal(e) - lock.unlock() - } else { - state = .terminal(nil) - lock.unlock() - } - case .terminal: - let pending = self.pending - lock.unlock() - pending.resumeAllWithNil() - } - } - - func cancel() { - lock.lock() - let pending = self.pending - guard case .subscribed(let subscription) = state else { - state = .terminal(nil) - lock.unlock() - pending.resumeAllWithNil() - return - } - state = .terminal(nil) - lock.unlock() - subscription.cancel() - pending.resumeAllWithNil() - } - - func next() async throws -> Input? { - return try await withUnsafeThrowingContinuation { continuation in - lock.lock() - switch state { - case .awaitingSubscription: - pending.append(continuation) - pendingDemand += 1 - lock.unlock() - case .subscribed(let subscription): - pending.append(continuation) - lock.unlock() - subscription.request(.max(1)) - case .terminal(nil): - lock.unlock() - continuation.resume(returning: nil) - case .terminal(let error?): - state = .terminal(nil) - lock.unlock() - continuation.resume(throwing: error) - } - } - } - } -} - -extension Sequence { - fileprivate func resumeAllWithNil() - where Element == UnsafeContinuation { - for continuation in self { - continuation.resume(returning: nil) - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Logger.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Logger.swift deleted file mode 100644 index 889c3d2..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Logger.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 20/01/2023. -// - -import Foundation -import os - -struct L { - @inline(__always) - static var enabled: Bool { - return false - } - - let subsystem: String - let category: String - - private let shouldLog: Bool - - init( - subsystem: String = "com.nordicsemi.ios_ble_library", category: String, - enabled: Bool = false - ) { - self.subsystem = subsystem - self.category = category - self.shouldLog = enabled - } - - func i(_ msg: String) { - #if DEBUG - if !shouldLog { return } - os_log("%@", type: .info, msg) - #endif - } - - func d(_ msg: String) { - #if DEBUG - if !shouldLog { return } - os_log("%@", type: .debug, msg) - #endif - } - - func e(_ msg: String) { - #if DEBUG - if !shouldLog { return } - os_log("%@", type: .error, msg) - #endif - } - - func f(_ msg: String) { - #if DEBUG - if !shouldLog { return } - os_log("%@", type: .fault, msg) - #endif - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/ContinuationSubscriber.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/ContinuationSubscriber.swift deleted file mode 100644 index 18d98a8..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/ContinuationSubscriber.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 05/05/2023. -// - -import Combine -import Foundation - -class ContinuationSubscriber: Subscriber { - - typealias Input = Upstream.Output - typealias Failure = Upstream.Failure - - private let continuation: CheckedContinuation - private var state: State = .waitingForSubscription - private var lock = NSLock() - private var subscription: Subscription? - - enum State { - case waitingForSubscription - case receivedSubscription - case terminated - } - - init(continuation: CheckedContinuation) { - self.continuation = continuation - } - - func receive(subscription: Subscription) { - lock.lock() - guard case .waitingForSubscription = state else { - lock.unlock() - return - } - - self.state = .receivedSubscription - self.subscription = subscription - lock.unlock() - - subscription.request(.max(1)) - } - - func receive(_ input: Upstream.Output) -> Subscribers.Demand { - lock.lock() - guard case .receivedSubscription = state else { - lock.unlock() - return .none - } - self.state = .terminated - continuation.resume(returning: input) - - self.subscription?.cancel() - lock.unlock() - - return .none - } - - func receive(completion: Subscribers.Completion) { - lock.lock() - guard case .receivedSubscription = state else { - lock.unlock() - return - } - - self.state = .terminated - - switch completion { - case .finished: - break - case .failure(let failure): - continuation.resume(throwing: failure) - } - lock.unlock() - } -} - -extension ContinuationSubscriber { - static func withCheckedContinuation(_ upstream: Upstream) async throws - -> Input where Upstream.Output == Input, Upstream.Failure == Failure - { - - try await withCheckedThrowingContinuation { c in - upstream.subscribe(ContinuationSubscriber(continuation: c)) - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Bluetooth.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Bluetooth.swift deleted file mode 100644 index 8b72a6f..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Bluetooth.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 05/05/2023. -// - -import Combine -import Foundation - -extension Publisher { - func bluetooth(_ fire: @escaping () -> Void) - -> Publishers.BluetoothPublisher - { - Publishers.BluetoothPublisher(self, fire: fire) - } -} - -extension Publishers { - public class BluetoothPublisher: ConnectablePublisher { - - private let inner: BaseConnectable - - init( - _ publisher: PublisherType, fire: @escaping () -> Void - ) where Output == PublisherType.Output, Failure == PublisherType.Failure { - self.inner = ClosureConnectablePublisher(upstream: publisher, fire: fire) - } - - public func receive(subscriber: S) - where S: Subscriber, Failure == S.Failure, Output == S.Input { - inner.receive(subscriber: subscriber) - } - - public func connect() -> Cancellable { - return inner.connect() - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Connectable.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Connectable.swift deleted file mode 100644 index 4df3440..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Connectable.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 05/05/2023. -// - -import Combine -import Foundation - -class BaseConnectable: ConnectablePublisher { - func connect() -> Cancellable { - fatalError() - } - - func receive(subscriber: Downstream) - where Failure == Downstream.Failure, Output == Downstream.Input { - fatalError() - } -} - -class ClosureConnectablePublisher: BaseConnectable< - Upstream.Output, Upstream.Failure -> -{ - typealias Output = Upstream.Output - typealias Failure = Upstream.Failure - - let upstream: Upstream - let fire: () -> Void - let onCancel: (() -> Void)? - - init(upstream: Upstream, fire: @escaping () -> Void, onCancel: (() -> Void)? = nil) { - self.upstream = upstream - self.fire = fire - self.onCancel = onCancel - } - - override func receive(subscriber: S) - where S: Subscriber, Failure == S.Failure, Output == S.Input { - upstream.subscribe(subscriber) - } - - override func connect() -> Cancellable { - fire() - return Cancelator(onCancel: onCancel) - } -} - -extension ClosureConnectablePublisher { - struct Cancelator: Cancellable { - let onCancel: (() -> Void)? - - func cancel() { - onCancel?() - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+FailablePrefix.swift.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+FailablePrefix.swift.swift deleted file mode 100644 index 1334e48..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+FailablePrefix.swift.swift +++ /dev/null @@ -1,128 +0,0 @@ -// -// Publishers+FailablePrefix.swift -// -// -// Created by Nick Kibysh on 22/08/2023. -// - -import Combine -import Foundation - -extension Publisher { - func prefix( - untilUntilOutputOrCompletion publisher: Other - ) -> Publishers.PrefixUntilOutputOrCompletion { - return .init(upstream: self, other: publisher) - } -} - -extension Publishers { - struct PrefixUntilOutputOrCompletion: Publisher - where Other.Failure == Upstream.Failure { - - public typealias Output = Upstream.Output - public typealias Failure = Upstream.Failure - - public let upstream: Upstream - public let other: Other - - public init(upstream: Upstream, other: Other) { - self.upstream = upstream - self.other = other - } - - public func receive(subscriber: Downstream) - where Downstream.Failure == Failure, Downstream.Input == Output { - upstream.subscribe(Inner(downstream: subscriber, trigger: other)) - } - } -} - -#warning("Thread safety should be considered") -extension Publishers.PrefixUntilOutputOrCompletion { - private final class Inner: Subscriber, Subscription - where Downstream.Input == Upstream.Output, Downstream.Failure == Upstream.Failure { - typealias Input = Upstream.Output - typealias Failure = Upstream.Failure - - private struct Termination: Subscriber { - - let inner: Inner - - var combineIdentifier: CombineIdentifier { - return inner.combineIdentifier - } - - func receive(subscription: Subscription) { - inner.terminationReceive(subscription: subscription) - } - - func receive(_ input: Other.Output) -> Subscribers.Demand { - return inner.terminationReceive(input) - } - - func receive(completion: Subscribers.Completion) { - inner.terminationReceive(completion: completion) - } - } - - private var subscription: Subscription? - private var termination: Termination? - private var terminationSubscription: Subscription? - private let downstream: Downstream - - init(downstream: Downstream, trigger: Other) { - self.downstream = downstream - let termination = Termination(inner: self) - self.termination = termination - trigger.subscribe(termination) - } - - func receive(subscription: Subscription) { - self.subscription = subscription - downstream.receive(subscription: self) - } - - func receive(_ input: Input) -> Subscribers.Demand { - return downstream.receive(input) - } - - func receive(completion: Subscribers.Completion) { - terminationSubscription?.cancel() - termination = nil - subscription = nil - downstream.receive(completion: completion) - } - - func request(_ demand: Subscribers.Demand) { - subscription?.request(demand) - } - - func cancel() { - subscription?.cancel() - terminationSubscription?.cancel() - } - - // MARK: - Private - - private func terminationReceive(subscription: Subscription) { - terminationSubscription = subscription - subscription.request(.max(1)) - } - - private func terminationReceive(_ input: Other.Output) -> Subscribers.Demand { - terminate(.finished) - return .none - } - - private func terminationReceive(completion: Subscribers.Completion) { - terminate(completion) - } - - private func terminate(_ completion: Subscribers.Completion) { - terminationSubscription?.cancel() - self.subscription?.cancel() - downstream.receive(completion: completion) - } - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+GuestList.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+GuestList.swift deleted file mode 100644 index 400f839..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+GuestList.swift +++ /dev/null @@ -1,121 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 01/05/2023. -// - -import Combine - -extension Publishers { - struct GuestList: Publisher where Upstream: Publisher { - typealias Output = Upstream.Output - typealias Failure = Upstream.Failure - - private let list: [Guest] - private let check: (Guest, Output) -> Bool - private let upstream: Upstream - - init(upstream: Upstream, list: [Guest], check: @escaping (Guest, Output) -> Bool) { - self.list = list - self.check = check - self.upstream = upstream - } - - init(upstream: Upstream, list: [Guest]) where Guest == Output { - self.list = list - self.check = { g, o in g == o } - self.upstream = upstream - } - - init(upstream: Upstream, list: [Guest], keypath: KeyPath) { - self.list = list - self.check = { g, o in o[keyPath: keypath] == g } - self.upstream = upstream - } - - func receive(subscriber: S) - where S: Subscriber, Upstream.Failure == S.Failure, Upstream.Output == S.Input { - upstream.subscribe(Inner(downstream: subscriber, list: list, check: check)) - } - } -} - -extension Publishers.GuestList { - class Inner: Subscriber, Subscription - where - Downstream: Subscriber, Upstream.Output == Downstream.Input, - Upstream.Failure == Downstream.Failure - { - typealias Input = Upstream.Output - typealias Failure = Upstream.Failure - - private var list: [Guest] - private var subscription: Subscription? - private let check: (Guest, Upstream.Output) -> Bool - private let downstream: Downstream - private var demand: Subscribers.Demand = .unlimited - - init(downstream: Downstream, list: [Guest], check: @escaping (Guest, Input) -> Bool) - { - self.downstream = downstream - self.list = list - self.check = check - } - - func receive(subscription: Subscription) { - self.subscription = subscription - downstream.receive(subscription: self) - } - - func receive(_ input: Upstream.Output) -> Subscribers.Demand { - for guest in list.enumerated() { - if check(guest.element, input) { - list.remove(at: guest.offset) - demand = downstream.receive(input) - break - } - } - - if list.isEmpty { - downstream.receive(completion: .finished) - cancel() - } - - return demand - } - - func receive(completion: Subscribers.Completion) { - downstream.receive(completion: completion) - } - - func request(_ demand: Subscribers.Demand) { - self.demand = demand - subscription?.request(demand) - } - - func cancel() { - subscription?.cancel() - } - - } -} - -extension Publisher { - func guestList(_ list: [Guest], check: @escaping (Guest, Output) -> Bool) - -> Publishers.GuestList - { - Publishers.GuestList(upstream: self, list: list, check: check) - } - - func guestList(_ list: [Guest]) -> Publishers.GuestList - where Guest == Output { - Publishers.GuestList(upstream: self, list: list) - } - - func guestList(_ list: [Guest], keypath: KeyPath) - -> Publishers.GuestList - { - Publishers.GuestList(upstream: self, list: list, keypath: keypath) - } -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Peripheral.swift b/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Peripheral.swift deleted file mode 100644 index 99f8be2..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/Publishers/Publishers+Peripheral.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 25/04/2023. -// - -import Combine -import CoreBluetoothMock -import Foundation - -extension Publisher where Output == CBPeripheral, Failure == Error { - func peripheral(_ fire: @escaping () -> Void) -> Publishers.Peripheral { - Publishers.Peripheral(self, fire: fire) - } -} - -extension Publishers.Peripheral { - var value: Output { - get async throws { - try await ContinuationSubscriber.withCheckedContinuation(self) - } - } -} - -extension Publishers { - public class Peripheral: ConnectablePublisher { - public typealias Output = CBPeripheral - public typealias Failure = Error - - private let inner: BaseConnectable - - init( - _ publisher: PublisherType, fire: @escaping () -> Void - ) where Output == PublisherType.Output, Failure == PublisherType.Failure { - self.inner = ClosureConnectablePublisher(upstream: publisher, fire: fire) - } - - public func receive(subscriber: S) - where S: Subscriber, Failure == S.Failure, CBPeripheral == S.Input { - inner.receive(subscriber: subscriber) - } - - public func connect() -> Cancellable { - return inner.connect() - } - } - - public class Service: ConnectablePublisher { - public typealias Output = CBService - public typealias Failure = Error - - private let inner: BaseConnectable - - init( - _ publisher: PublisherType, fire: @escaping () -> Void - ) where Output == PublisherType.Output, Failure == PublisherType.Failure { - self.inner = ClosureConnectablePublisher(upstream: publisher, fire: fire) - } - - public func receive(subscriber: S) - where S: Subscriber, Failure == S.Failure, CBService == S.Input { - inner.receive(subscriber: subscriber) - } - - public func connect() -> Cancellable { - return inner.connect() - } - } - -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/RSSI.swift b/Sources/iOS-BLE-Library-Mock/Utilities/RSSI.swift deleted file mode 100644 index 3d3a2cd..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/RSSI.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// RSSI.swift -// iOS-BLE-Library -// -// Created by Dinesh Harjani on 23/8/22. -// - -import Foundation - -// MARK: - RSSI - -private struct Const { - public static let outOfRange = 127 - public static let practicalWorst = -100 - public static let bad = -90 - public static let ok = -80 - public static let good = 50 -} - -public struct RSSI: ExpressibleByIntegerLiteral, Equatable, Hashable { - - public enum Signal { - case outOfRange - case practicalWorst - case bad - case ok - case good - - init(rssi: Int) { - switch rssi { - case let x where x == Const.outOfRange: self = .outOfRange - case let x where x < Const.bad: self = .practicalWorst - case let x where x < Const.ok: self = .bad - case let x where x < Const.good: self = .ok - default: self = .good - } - } - } - - public typealias IntegerLiteralType = Int - - // MARK: Properties - - public let value: Int - public let signal: Signal - - // MARK: Init - - public init(integerLiteral value: Int) { - self.value = value - self.signal = Signal(rssi: value) - } -} - -// MARK: - Constants - -extension RSSI { - - public static let outOfRange = RSSI(integerLiteral: Const.outOfRange) - public static let practicalWorst = RSSI(integerLiteral: Const.practicalWorst) - public static let bad = RSSI(integerLiteral: Const.bad) - public static let ok = RSSI(integerLiteral: Const.ok) - public static let good = RSSI(integerLiteral: Const.good) -} diff --git a/Sources/iOS-BLE-Library-Mock/Utilities/UnimplementedError.swift b/Sources/iOS-BLE-Library-Mock/Utilities/UnimplementedError.swift deleted file mode 100644 index 764bfbf..0000000 --- a/Sources/iOS-BLE-Library-Mock/Utilities/UnimplementedError.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// File.swift -// -// -// Created by Nick Kibysh on 16/08/2023. -// - -import Foundation - -func unimplementedError() -> Never { - fatalError("Unimplemented Method") -}