Giter Club home page Giter Club logo

rzbluetooth's People

Contributors

chrisballinger avatar cpatterson avatar cpatterson-lilly avatar dostrander avatar drdaz avatar felix-dumit avatar fluehre avatar gawetaner avatar gilesvangruisen avatar joshbrown-eg avatar kingofbrian avatar mplorentz avatar nickls avatar samdmarshall avatar tahirmt avatar yvettecook avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rzbluetooth's Issues

Replacement for Dynamic methods in RZBMockPeripheral?

Hi there

I just updated my version of RZBluetooth from 0.1 to 1.0.2. There were some pretty big changes, but the migration generally wasn't tough.

One thing I have lost now, that I'm not sure how to replicate, is that I was using the methods in the "Dynamic" interface extension to RZBMockPeripheral in my tests. In particular, I had implemented rzb_writeData... for my test to post a notification containing the packet being written, so that I could catch the packets sent in a flow and check they were correct at a later stage.

Is there any way I can achieve something similar now?

Support for simulated peripherals

Issue

RZBluetooth offers powerful tools for simulating Bluetooth device interactions. At this time, they mostly serve in-app testing use cases. We would benefit from the ability to test production apps, and companion apps on other platforms, using the same mock peripheral. This issue proposes adding support for simulated Bluetooth devices. Ideally, a mock peripheral could emit real Bluetooth events for other Bluetooth consumers rather than invoke CoreBluetooth callbacks on the hosting app.

Feedback welcome.

Allow opting in for CBCentralManagerOptionShowPowerAlertKey

Hey! Love this library, great work =)
I had the need to show a warning when bluetooth is disabled, CBCentralManager happens to have a handy option that does just that. It would be nice to be able to hint to RZBluetooth to including it during initialization!

Mocking cannot be enabled and disabled on demand

Issue

Enabling and disabling RZBluetooth mocking is not working as I expect.

Steps to reproduce:

  1. Enable mocking (RZBEnableMock(true))
  2. Disable mocking (RZBEnableMock(false))
  3. Enable mocking (RZBEnableMock(true))
  4. Instantiate a CBPeripheralManager

Expected: CBPeripheralManager.mock is not nil.
Actual: CBPeripheralManager.mock is nil.

Example

I've attached and example project. It is a mixed Objective-C/Swift framework project since that is how my current project is set up. You can demonstrate the issue by running the tests.
RZBDisableMockExample.zip

Regression calling stopScan in RZBSimulatedTestCase context between 1.0.2 and 1.2.2

Hi there

I recently upgraded a project I'm working on from 1.0.2 to 1.2.2, and tried to run my test suite on my BT logic. One of my tests (which simulates a connection timeout) crashes in RZBSimulatedCallback.m at line 118.

Here's my test to reproduce in an RZBSimulatedTestCase:

-(void)testRZBFail
{
    [self.device addHopGateService];
    
    [self.centralManager scanForPeripheralsWithServices:@[[CBUUID hopspotsServiceUUID]] options:nil onDiscoveredPeripheral:^(RZBScanInfo * _Nullable scanInfo, NSError * _Nullable error) {
        
    }];
    
    XCTestExpectation *expect = [self expectationWithDescription:@"Complete"];
    
    [[BFTask taskWithDelay:5] continueWithBlock:^id _Nullable(BFTask * _Nonnull t) {
        [self.centralManager stopScan];
        [expect fulfill];
        return nil;
    }];
    [self waitForExpectations:@[expect] timeout:10];
    
}

If you're not familiar with Bolts / BFTask, it's an implementation of Futures / Promises. What I'm doing here is waiting 5 seconds after we start scanning to call stopScan; if I call it instantly we don't crash.

The addHopGateService method is in a category on RZBSimulatedDevice. It looks like this:

-(void)addHopGateService
{
    CBMutableService *hopGateService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:hopspotsServiceUUIDString] primary:YES];

    CBMutableCharacteristic *hopSpotEventNotifyCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:hopspotsReceiveCharacteristicUUIDString]
                                                                                                   properties:CBCharacteristicPropertyRead | CBCharacteristicPropertyIndicate | CBCharacteristicPropertyNotify
                                                                                                        value:nil
                                                                                                  permissions:CBAttributePermissionsReadable];
    CBMutableCharacteristic *hopSpotControlCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:hopspotsTransmitCharacteristicUUIDString]
                                                                                               properties:CBCharacteristicPropertyWrite
                                                                                                    value:nil
                                                                                              permissions:CBAttributePermissionsWriteable];
    hopGateService.characteristics = @[hopSpotEventNotifyCharacteristic, hopSpotControlCharacteristic];
    [self addService:hopGateService];
}

This should be enough to reproduce the error. Let me know if I can help.

clearNotifyBlock should not `setNotify: NO` if disconnected

@cpatterson-lilly @KingOfBrian I am having a small problem... I have a peripheral that always automatically reconnects if I manually call cancelConnectionWithCompletion:. I have maintainConnection false for this peripheral right now. Since maintainConnection is false the connectWithCompletion: method is not called from within connectionEvent:error:

I see the following in the logs

RZBLogLevelCommand - cancelPeripheralConnection: <PERIPHERAL UUID>
RZBLogLevelDelegate - centralManager:didDisconnectPeripheral:error: - <CBCentralManager: 0x17007dd80> <PERIPHERAL UUID> (null)
RZBLogLevelCommand - connectPeripheral:<PERIPHERAL UUID> options:(null)
RZBLogLevelDelegate - centralManager:didConnectPeripheral: - <CBCentralManager: 0x17007dd80> <PERIPHERAL UUID>

I must mention however that I have another peripheral that I do not see the same behavior on. The same peripheral behaves normally using CoreBluetooth directly.

It appears that after calling disconnect executeCommandWithContext:error: is still called on an RZBConnectCommand instance

sorry about question

there is no issue, but, do you have an example of restorationHandler use?

sorry, thanks in advance

Add iOS Simulator support via Mac helper app

I'd really love to add Core Bluetooth support to the simulator. It should be pretty straight forward to do by relaying the commands and callbacks over an HTTP socket to a mac process running locally. The API's are identical even if the performance and error conditions are a bit different.

From an implementation PoV, this is basically a mock central manager that talks to a mac app instead of the in-memory simulation.

-mockPeripheral:readValueForCharacteristic: method caches char value

Just a question here related to using the mock objects for unit testing.

I see that the method RZBSimulatedConnection -mockPeripheral:readValueForCharacteristic: will never call through to any readHandlers set up on the RZBSimulatedDevice object once a characteristic value has been set (i.e. once if (characteristic.value == nil) is false).

Is this how a real Core Bluetooth connection works?

I know that when you use a nil characteristic value when initializing a CBMutableCharacteristic, the Apple docs say:

If nil, the value will be dynamic and requested on-demand.

...but it doesn't specify what happens the second time a central reads its value.

I'd like my mock read handlers to be called every time the characteristic is read during unit testing.

Add better swift support by using class properties, nullable, nonnull and NSDicationary and NSArray with types

Right now RZBluetooth is great for use in Objective-C but when you use it in Swift you see a lot of [Any]!, [AnyHashable: Any] and String! and also class func instead of var.

To improve this what needs to be done is

  • Add nullable and nonnull to properties and method params where applicable. (nonatomic, nullable, copy)
  • Add type to NSArray and NSDictionary properties, e.g. NSArray <NSString *>*
  • Replace many of the class methods like + (CBUUID *)serviceUUID; to @property (nonatomic, strong, readonly, nonnull, class) CBUUID *serviceUUID;

Doing all this will make the library look and feel native to Swift when used while it would primarily remain unaffected to Objective-C. I know it is a massive change and will require a major version bump and migration notes.

@KingOfBrian do you think this is a good change and should be done?

When you call rzb_fetchDeviceInformationKeys it does not actually read the first time

After connection at some point I am calling rzb_fetchDeviceInformationKeys to read device information. However, the first call to this always returns nil values as it doesn't actually read the values

I see the following logs

RZBLogLevelCommand - <DEVICE UUID> discoverServices:@[Device Information]

RZBLogLevelDelegate - peripheral:didDiscoverServices: - <DEVICE UUID> (null)

RZBLogLevelDelegateValue - Services=@[180A]

RZBLogLevelCommand - <DEVICE UUID> discoverCharacteristics:@[System ID, Model Number String, Serial Number String, Software Revision String, Firmware Revision String, Hardware Revision String, Manufacturer Name String, PnP ID] forService:180A

RZBLogLevelDelegate - peripheral:didDiscoverCharacteristicsForService:error: - <DEVICE UUID> (null)

RZBLogLevelDelegateValue - Characteristics=@[2A29, 2A24, 2A25]

However, if I call it again once the characteristics are discovered it returns the values and I see logs like

RZBLogLevelCommand - <DEVICE UUID> readValueForCharacteristic:Model Number String

RZBLogLevelCommand - <DEVICE UUID> readValueForCharacteristic:Serial Number String

RZBLogLevelCommand - <DEVICE UUID> readValueForCharacteristic:Manufacturer Name String

RZBLogLevelCommand - <DEVICE UUID> discoverCharacteristics:@[System ID, Software Revision String, Firmware Revision String, Hardware Revision String, PnP ID] forService:180A

RZBLogLevelDelegate - peripheral:didUpdateValueForCharacteristic:error: - <DEVICE UUID> (null)

RZBLogLevelDelegateValue - Value=<some value>

RZBLogLevelDelegate - peripheral:didUpdateValueForCharacteristic:error: - <DEVICE UUID> (null)

RZBLogLevelDelegateValue - Value=<some value>

RZBLogLevelDelegate - peripheral:didUpdateValueForCharacteristic:error: - <DEVICE UUID> (null)

RZBLogLevelDelegateValue - Value=<some value>

RZBLogLevelDelegate - peripheral:didDiscoverCharacteristicsForService:error: - <DEVICE UUID> 180A (null)

RZBLogLevelDelegateValue - Characteristics=@[2A29, 2A24, 2A25]

My understanding was that RZBluetooth handles the discovery of services and characteristics internally and I do not need to call discoverService and discoverCharacteristics methods...

Crash in -[RZBPeripheral cancelAllCommands] due to infinite loop

Issue

In the completion block of -[RZBPeripheral connectWithCompletion:], call -[RZBPeripheral cancelConnectionWithCompletion:]. It'll create an infinite loop and eventually crash with an EXC_BAD_ACCESS.

For example:

- (void)testCancelConnectionInConnectCompletion {
    RZBPeripheral *peripheral = [self.centralManager peripheralForUUID:self.connection.identifier];

    [peripheral connectWithCompletion:^(NSError * _Nullable error) {
        [peripheral cancelConnectionWithCompletion:nil];
    }];
    
    [self waitForQueueFlush];
}

retrieveConnectedPeripheralsWithServices not implemented

There is no way for us to retrieve the currently connected peripherals. So for instance, if I am making two apps that use the same peripheral at on the same device I can use CBCentralManager's retrieveConnectedPeripheralsWithServices: method but there is no support for that on RZBluetooth yet

Simulating a scan and a connection

Hi there

One thing that isn't obvious to me from the documentation is how to simulate a scan followed by a connection.

I'm using a RZBSimulatedTestCase subclass. I'm calling my service layer, which initiates a scan. I have added services to my simulated device and called startAdvertising before the scan starts. But the scan never finds a device.

What steps am I missing?

Unable to connect to peripheral if maintainConnection = TRUE and Bluetooth is power cycled

  • scanForPeripheralsWithServices:
  • readCharacteristicUUID
  • enableNotifyForCharacteristicUUID:
  • characteristic is updated
  • power off peripheral
  • after RZBPeripheralStateEventDisconnected, set self.peripheral.maintainConnection = TRUE;
  • Bluetooth Off/On
  • After CBManagerStatePoweredOn:, scanForPeripheralsWithServices:
  • peripheral is found but readCharacteristicUUID always times out with no connection

Add a way to cancel all pending commands for a peripheral

In our app, once the user "pairs" to a peripheral, we use a connectionDelegate to re-issue a characteristic write as soon as it disconnects, which works great. Later, in the UI the user can also manually forget a peripheral, and at this point I was calling RZBPeripheral.cancelConnection.

However, I noticed that if I had that pending write command for that peripheral, the library would pro-actively retry the connection command, which would leave a dangling connection request for a device that the app has no interest in anymore, and that was causing some issues. I searched for a way to cancel all outstanding commands for a peripheral but didn't find anything, so I ended up hacking that feature into RZBPeripheral with an extension:

@implementation RZBPeripheral (Cancellation)

- (RZBCommandDispatch *)hs_dispatch {
    SEL selector = NSSelectorFromString(@"dispatch");
    IMP imp = [self methodForSelector:selector];
    RZBCommandDispatch* (*func)(id, SEL) = (void *)imp;
    return func(self, selector);
}

- (void)hs_cancelAllCommands {
    NSArray *commands = [self.hs_dispatch commandsOfClass:nil
                                 matchingUUIDPath:RZBUUIDP(self.identifier)
                                       isExecuted:YES];
    
    NSError *error = [NSError errorWithDomain:[NSString stringWithFormat:@"%@-hs", RZBluetoothErrorDomain]
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"The operation has been manually cancelled by the user"}];
    
    for (RZBCommand *command in commands) {
        [self.hs_dispatch completeCommand:command withObject:nil error:error];
    }
}

@end

I feel like this is a useful feature to add to the library, do you agree? If so, what do you think of this solution? If you think this approach is fine I can submit a PR with this change.

Improve Documentation for Mocking and Testing

Hey @drdaz,
Thanks for asking the questions! Let's keep tracking things here just to keep things in the open. You can also email me at [email protected] if you have any questions that you don't want tracked publicly.

Your question WRT advertisedServices is a good catch. You must set it to an array of CBUUID's that you want to be in the advertisement packet. This usually includes the primary service UUIDs, but not less interesting ones (IE: The battery).

I'm working on a documentation update for this, but I don't have a big window of time, so any help would be great.

Crash in RZBPeripheral -clearNotifyBlockForCharacteristicUUID:serviceUUID:completion:

If an invalid characteristicUUID parameter is passed to the above method, the app will crash because the underlying method -setNotifyBlock:forCharacteristicUUID: is passed a nil UUID which it tries to use as a key in the notifyBlocks dictionary.

This can happen if an app tries to clear notify blocks (ie. unregister notification/indication observers) immediately in response to a peripheral disconnection.

Pull request coming soon...

Remove advertisedServices

I believe we can remove advertisedServices and check for isPrimary on the services array instead.

Build warnings using CocoaPods

I recently upgraded my xcodeproj gem, which has made building projects using CocoaPods much more... sensitive. I now (and perhaps previously) have the following warnings from RZBluetooth.

Most of it can be fixed by updating the deployment target of RZBluetooth to 7.1 I think.

screen shot 2018-05-17 at 17 44 50

Unable to integrate into iOS unit test target

I've been struggling for a while to integrate the testing library into a unit test target, but I haven't found a solution that works yet.

Given a pod file like the following :

platform :ios, '10.0'

use_frameworks!

target 'tacx' do
    
    pod 'RZBluetooth/Mock'
    
    target 'tacxTests' do
        inherit! :search_paths
        pod 'RZBluetooth/Test'
    end

end

A unit testing file:

@import RZBluetooth;

@interface tacxTests : RZBSimulatedTestCase
@end

@implementation tacxTests
@end

Everything compiles just fine, but when running the test, it prints a message stating :

2016-11-20 19:05:59.112 tacx[95628:5961223] Failed to load test bundle from file:///Users/xxxxxxx/Library/Developer/Xcode/DerivedData/tacx-ghqgcpxbcphxnmhievkvngfndhzq/Build/Intermediates/CodeCoverage/Products/Debug-iphonesimulator/tacx.app/PlugIns/tacxTests.xctest/: Error Domain=NSCocoaErrorDomain Code=3588 "dlopen(/Users/xxxxxxx/Library/Developer/Xcode/DerivedData/tacx-ghqgcpxbcphxnmhievkvngfndhzq/Build/Intermediates/CodeCoverage/Products/Debug-iphonesimulator/tacx.app/PlugIns/tacxTests.xctest/tacxTests, 265): Symbol not found: OBJC_CLASS$_RZBSimulatedTestCase
Referenced from: /Users/xxxxxxx/Library/Developer/Xcode/DerivedData/tacx-ghqgcpxbcphxnmhievkvngfndhzq/Build/Intermediates/CodeCoverage/Products/Debug-iphonesimulator/tacx.app/PlugIns/tacxTests.xctest/tacxTests
Expected in: /Users/xxxxxxx/Library/Developer/CoreSimulator/Devices/D4A4BA5A-3023-4B18-B912-EE1FCDA1835D/data/Containers/Bundle/Application/2060D194-B119-4FEA-A8B1-917686855AB4/tacx.app/Frameworks/RZBluetooth.framework/RZBluetooth
in

As you can see by the Referenced from and Expected in lines, Xcode sees a symbol from the RZBluetooth framework (_OBJC_CLASS_$_RZBSimulatedTestCase) and expects it to be located in /tacx.app/Frameworks/RZBluetooth.framework/RZBluetooth, which is the app's version of RZBluetooth (created by pod 'RZBluetooth/Mock' in the podfile). This causes the app to crash because it can not find a the symbols for RZBSimulatedTestCase.

Are you aware of any way to explicitly importing the unit test's version of the library? (that is the version of the library that contains the RZBSimulatedTestCaseclass?

WARNING: The delegate for CBPeripheral does not implement -[peripheral:didModifyServices:]

Issue

The error message in the title is generated by Core Bluetooth when a central app using RZBluetooth is communicating with a peripheral device that changes its services database while connected.

This is most easily seen in the log output of a central app, when an app using CBPeripheralManager to simulate the peripheral is quit while the central app is still connected to it.

RZBCentralManager needs to add the delegate method in question, if only to log what happened. Ideally it would also invalidate the peripheral's services and characteristics so they would be re-discovered on the next command.

Bluetooth Logs

2017-03-10 14:26:29.769412 AppName[722:459894] [CoreBluetooth] WARNING: The delegate for <CBPeripheral: 0x1700f7480, identifier = 5099F44B-85BE-83AC-8387-C1D6E4E7A57D, name = DeviceName, state = connected> does not implement -[peripheral:didModifyServices:]

RZBEnableMock not working from Swift Code

I am invoking RZBEnableMock(YES) from "load" method in Objective C. This category is included in a Swift project, but "RZBEnableMock" is not working. It is not able to Swizzle alloc method of CBCentralManager.

Crash in -[RZBPeripheral clearNotifyBlocks]

Issue

The clearNotifyBlocks method in RZBPeripheral crashes when there's at least one key/value pair in the self.notifyBlockByUUIDs NSMutableDictionary.

The reason is that clearNotifyBlocks iterates over the self.notifyBlockByUUIDs dictionary, calling clearNotifyBlockForKey:. In clearNotifyBlockForKey:, the key/value pair is removed from self.notifyBlockByUUIDs. This crashes since we're removing an object from the dictionary we're currently iterating over. Here's the error message:

Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSDictionaryM: 0x7f9f31a84440> was mutated while being enumerated.'

The fix is straightforward and just requires a small change to clearNotifyBlocks:

- (void)clearNotifyBlocks {
    NSArray *keys = self.notifyBlockByUUIDs.allKeys;
    for (NSString* key in keys) {
        [self clearNotifyBlockForKey:key];
    }
}

I have this fixed locally and may be able to create a PR soon, but I wanted to document the problem and solution just in case I don't get to it right away.

Disconnection, Reconnection and state restoration

As mentioned in #57, i'm trying out your framework with the heartrate example. I'm quite new to iOS so it's difficult for me to understand everything.

What I have found is that once I am connected to a device, if I move back from the service list view, the sensor stays connected. I would have expected it to disconnect at that point. When I go back to the "scan" view the device is not visible as it's still connected and therefore not advertising. I don't know if what I am seeing is a consequence of the fixes i put in place for #57? Could you advise please?

What I would like to evaluate, is making my app robust to the device disappearing (temporarily out of range) or my app being closed by iOS due to memory pressure etc. I essentially want my app to be always running unless I intentionally kill it. Can you advise on what I need to do to achieve that with your framework? I think some of the key elements are already there?

Thanks

Type support for arrays/dictionary for Swift

You can't tell what is in the array without looking at the implementation. So things like:
scanForPeripheralsWithServices: it's not clear what should go into the array (CBUUID or NSUUID).

If we just add <CBUUID> it should clear this up. We should also do this for optionals

Convert to CBManagerState for iOS 10.

In the iOS 10 SDK, they've combined CBCentralManagerState and CBPeripheralManagerState into one type called CBManagerState.

Currently, building RZBluetooth in Xcode 8/iOS 10 SDK throws warnings about the type conversion:

warning: implicit conversion from enumeration type 'CBManagerState' (aka 'enum CBManagerState') to different enumeration type 'CBCentralManagerState' (aka 'enum CBCentralManagerState') [-Wenum-conversion]

OSX Support

It'd be nice to have OSX support especially for the Mock framework so we can have the a simulator and a command line peripheral/central at the same time.

If a peripheral disconnects while the scan that found it is running, the scan will fail

Issue

I've noticed that if I connect to a peripheral that was found in a scan operation, and if the peripheral disconnects from us while the scan is still running, the scan will fail with the disconnection error being passed to the scan block. There are ways to work around this in my code, but this behavior seems weird to me.

Bluetooth Logs

The disconnection error is logged as follows:

centralManager:didDisconnectPeripheral:error: - <CBCentralManager: 0x174265200> 0D789391-1F42-41AB-82D8-3A431C753018 Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us." UserInfo={NSLocalizedDescription=The specified device has disconnected from us.}

Manually prompt discovery of characteristics?

Hey there

Is there some way of manually making characteristic discovery happen? I have a write characteristic that I'd like to have discovered before I write to it...

And a hacky solution is fine, because I'm writing a hack.

Unable to reconnect peripheral after Bluetooth reset

If the Bluetooth radio resets during a connection, I am unable to reconnect to the peripheral. The only solution is to hard reset the phone.

CBCentralState 5 -> 1

[V][2017/05/24 11:55:16:268][AppDelegate -[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke][Line 57] peripheral:didUpdateValueForCharacteristic:error: - 4A571E85-188E-44CF-A03F-4DDDD152BA7E 914214C5-825C-4D8F-BD37-570F6BD532CD (null)
[V][2017/05/24 11:55:16:750][AppDelegate -[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke][Line 57] 4A571E85-188E-44CF-A03F-4DDDD152BA7E readValueForCharacteristic:914214C5-825C-4D8F-BD37-570F6BD532CD
[V][2017/05/24 11:55:17:774][AppDelegate -[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke][Line 57] centralManagerDidUpdateState: - <CBCentralManager: 0x170275640>
[V][2017/05/24 11:55:17:775][ViewController -[ViewController stateChange]][Line 352]
[V][2017/05/24 11:55:17:775][ViewController -[ViewController stateChange]][Line 353] centralManager.state = 1
[V][2017/05/24 11:55:17:775][ViewController -[ViewController stateChange]][Line 361] ERROR reset pause
[V][2017/05/24 11:55:17:797][ViewController -[ViewController stateChange]][Line 372] ERROR CBManagerStateResetting: The Bluetooth radio reset unexpectedly. btConnected = 1 scanning = 0
[V][2017/05/24 11:55:17:836][ViewController -[ViewController stateChange]_block_invoke][Line 384] cancelConnectionWithCompletion
[V][2017/05/24 11:55:21:251][AppDelegate -[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke][Line 57] centralManagerDidUpdateState: - <CBCentralManager: 0x170275640>
[V][2017/05/24 11:55:21:252][ViewController -[ViewController stateChange]][Line 352]
[V][2017/05/24 11:55:21:252][ViewController -[ViewController stateChange]][Line 353] centralManager.state = 5
[V][2017/05/24 11:55:21:267][AppDelegate -[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke][Line 57] connectPeripheral:4A571E85-188E-44CF-A03F-4DDDD152BA7E options:(null)

Attempt to connect fails

[V][2017/05/24 11:59:15:627][AppDelegate -[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke][Line 57] centralManager:didDiscoverPeripheral:advertisementData:RSSI: - <CBCentralManager: 0x170275640> 4A571E85-188E-44CF-A03F-4DDDD152BA7E
[V][2017/05/24 11:59:15:627][ViewController -[ViewController poweredOn]_block_invoke_2][Line 436] discovered Plug
[V][2017/05/24 11:59:15:628][ViewController -[ViewController poweredOn]_block_invoke_2][Line 456] peripheral = 4A571E85-188E-44CF-A03F-4DDDD152BA7E
[V][2017/05/24 11:59:15:628][ViewController -[ViewController poweredOn]_block_invoke_2][Line 457] services = (
)
[V][2017/05/24 11:59:15:628][ViewController -[ViewController poweredOn]_block_invoke_2][Line 458] readCharacteristicUUID:
[V][2017/05/24 11:59:15:628][ViewController -[ViewController readPowerReset]][Line 1326]
[V][2017/05/24 11:59:15:629][AppDelegate -[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke][Line 57] connectPeripheral:4A571E85-188E-44CF-A03F-4DDDD152BA7E options:(null)
...
[V][2017/05/24 11:59:46:491][ViewController -[ViewController poweredOn]_block_invoke_4][Line 464] Error Domain=com.raizlabs.bluetooth Code=7 "(null)"
[V][2017/05/24 11:59:46:491][ViewController -[ViewController readPowerReset]_block_invoke_2][Line 1332] Error Domain=com.raizlabs.bluetooth Code=7 "(null)"
[V][2017/05/24 11:59:46:492][ViewController -[ViewController readPowerReset]_block_invoke_2][Line 1332] Error Domain=com.raizlabs.bluetooth Code=7 "(null)"
[V][2017/05/24 11:59:46:492][ViewController -[ViewController poweredOn]_block_invoke_5][Line 473] cancelConnectionWithCompletion

issue with heartrate example

Hello. I may be misunderstanding something but I couldn't get your heartrate example to work without some modifications.

In ScanListViewController.m, when I was looking for my heartrate sensor, I would get the following error in viewWillAppear:

2017-04-07 17:01:28.751428 RZBluetoothExample[286:10774] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete row 0 from section 0 which only contains 0 rows before the update'

The first issue (i think) was that scannedDevices wasn't getting populated with anything from scanInfo, so I added the line:

[self.scannedDevices addObject:scanInfo];

just after

[self.centralManager scanForPeripheralsWithServices:@[self.scanUUID] options:@{} onDiscoveredPeripheral:^(RZBScanInfo *scanInfo, NSError *error) {
        if (error) {
            NSLog(@"Error scanning: %@", error);
            return;
        }

This was then correctly populating devices into scannedDevices but they weren't being shown (still the same error above) until I rewrote the populating of the list like this:

NSIndexPath *durPath = [NSIndexPath indexPathForRow:0 inSection:0];
            NSArray *paths = [NSArray arrayWithObject:durPath];
            [self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationNone];

instead of:

[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:existingIndex inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];

I know that this isn't as elegant, but it worked and I now get heartrate data. The full method is this:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.centralManager scanForPeripheralsWithServices:@[self.scanUUID] options:@{} onDiscoveredPeripheral:^(RZBScanInfo *scanInfo, NSError *error) {
        if (error) {
            NSLog(@"Error scanning: %@", error);
            return;
        }
        
        [self.scannedDevices addObject:scanInfo];
        
        __block NSUInteger existingIndex = 0;
        [self.scannedDevices enumerateObjectsUsingBlock:^(RZBScanInfo * info, NSUInteger idx, BOOL * _Nonnull stop) {
            if ([info.peripheral.identifier isEqual:scanInfo.peripheral.identifier]) {
                info.advInfo = scanInfo.advInfo;
                info.RSSI = scanInfo.RSSI;
                existingIndex = idx;
            }
        }];
        
        NSLog(@"%@ - %@", [scanInfo.peripheral.identifier UUIDString], scanInfo.advInfo);

        if (existingIndex == NSNotFound) {
            [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.scannedDevices.count - 1 inSection:0]]
                                  withRowAnimation:UITableViewRowAnimationAutomatic];
        }
        else {
            NSLog(@"numberOfRowsInSection: %ld", (long)[self tableView:self.tableView numberOfRowsInSection:0]);
            
            NSIndexPath *durPath = [NSIndexPath indexPathForRow:0 inSection:0];
            NSArray *paths = [NSArray arrayWithObject:durPath];
            [self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationNone];
            
            //[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:existingIndex inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
            

        }
    }];
}

However, did I do something wrong with your code, or is there a more elegant way of achieving what I "fixed"?

I have a follow-on question which I will post in a second thread.

Thanks

cancelConnection fires completion handler before disconnection

I suspect this isn't an issue with your library per se; I'm seeing the same behaviour with similar libraries. But I've been banging my head against this for a little while and I'd like to share the problem.

When the completion handler for cancelConnectionFromPeripheralUUID fires, I expect (wrongly) the peripheral to have actually disconnected. If I attempt to reconnect at this point, things get generally messy.

Is there any way of being notified when we actually have finished disconnecting? KVO on CBPeripheral's state variable perhaps?

Project deployment target doesn't match Podspec

Issue

The podspec lists the iOS deployment target at 9.0, but the Xcode project lists the iOS deployment target at 8.4.

Is there any reason for this? Can the podspec's deployment target be dropped down to 8.4, or can they potentially be both dropped even further to 8.0?

Thanks!

RZBEnableMock() doesn't work in Swift

It seems that RZBEnableMock() doesn't work when used in pure Swift code.

This came up while we were writing some peripheral simulator unit tests.

Try pasting the following code into the file SwiftTestCase.swift:

class SwiftTestCase2: XCTestCase {
    
    func testMocking() {
        var centralManager = CBCentralManager()
        XCTAssertNil(centralManager.mock)
        
        var peripheralManager = CBPeripheralManager()
        XCTAssertNil(peripheralManager.mock)

        // Enable RZBluetooth mocking
        RZBEnableMock(true)
        
        centralManager = CBCentralManager()
        XCTAssertNotNil(centralManager.mock) // Fails
        
        peripheralManager = CBPeripheralManager()
        XCTAssertNotNil(peripheralManager.mock) // Fails
        
        // Restore normal operation
        RZBEnableMock(false)
        
        centralManager = CBCentralManager()
        XCTAssertNil(centralManager.mock)
        
        peripheralManager = CBPeripheralManager()
        XCTAssertNil(peripheralManager.mock)
    }
}

Apparently, Swift's static typing isn't fooled by swizzling the Obj-C alloc method...

API MISUSE: <private> has no restore identifier but the delegate implements the centralManager:willRestoreState: method. Restoring will not be supported

I just updated to Xcode 8, and I'm now seeing the following warning:

"API MISUSE: has no restore identifier but the delegate implements the centralManager:willRestoreState: method. Restoring will not be supported"

I'm not sure how dangerous this is; I'm guessing the issue was there the whole time but I wasn't seeing the warning. Thought you should know about it though.

Swift support in RZBPeripheralConnectionDelegate

Just migrated my code to 1.0, and ran into this issue.

The declaration of the RZBPeripheralConnectionDelegate protocol method:

- (void)peripheral:(RZBPeripheral *)peripheral connectionEvent:(RZBPeripheralStateEvent)event error:(NSError *)error;

...needs the error parameter to be __nullable to work properly with Swift. Currently, since the error parameter is not Optional in Swift, it will cause a crash if the error is null.

For now, I'm working around the issue by declaring my implementation of the delegate method with an NSError? parameter, but that generates a build warning.

CocoaPods issue: RZBTestDefines.h file not found when using "RZBluetooth/Test" pod

New issue with the 10/10 commit:

I'm using RZBSimulatedTestCase in my unit tests, and it appears that CocoaPods does not pull down the new file RZBTestDefines.h when I include the "RZBluetooth/Test" pod in my test target in my Podfile. Thus my test target build fails.

I'm guessing that line 43 of the podspec should change from:

    test.public_header_files = "RZBluetoothTests/RZBSimulatedTestCase.h"

to:

    test.public_header_files = "RZBluetoothTests/RZBSimulatedTestCase.h", 
                               "RZBluetoothTests/RZBTestDefines.h"

Should `enableNotify` continue to work after a peripheral reconnects?

Issue

Here's my scenario:

  • have a peripheral UUID that was discovered previously
  • as soon as the app is launched, obtain a new RZBPeripheral instance from RZBCentralManager and enable maintainConnection
  • call enableNotify and observe that updated values are received correctly
  • power cycle the peripheral
  • updated values are no longer received

I can work around this by setting a connectionDelegate and having it call enableNotify again once the connection succeeds, but should this be needed at all? From the docs I was expecting that the framework would handle this.

Bluetooth Logs

10:40:28.438 🔵DEBUG BluetoothController.init():55 - centralManagerDidUpdateState: - <CBCentralManager: 0x1720616c0>
10:40:28.439 🔵DEBUG BluetoothController.init():55 - State=5
10:40:28.439 🔵DEBUG BluetoothController.init():55 - connectPeripheral:89CF787A-F177-45F6-BE49-0F3DD92D7E20 options:(null)
10:40:28.757 🔵DEBUG BluetoothController.init():55 - centralManager:didConnectPeripheral: - <CBCentralManager: 0x1720616c0> 89CF787A-F177-45F6-BE49-0F3DD92D7E20
10:40:28.758 🔵DEBUG BluetoothController.init():55 - 89CF787A-F177-45F6-BE49-0F3DD92D7E20 discoverServices:@[181D]
10:40:28.831 🔵DEBUG BluetoothController.init():55 - peripheral:didDiscoverServices: - 89CF787A-F177-45F6-BE49-0F3DD92D7E20 (null)
10:40:28.831 🔵DEBUG BluetoothController.init():55 - Services=@[181D]
10:40:28.832 🔵DEBUG BluetoothController.init():55 - 89CF787A-F177-45F6-BE49-0F3DD92D7E20 discoverCharacteristics:@[2A9D] forService:181D
10:40:28.832 🔵DEBUG BluetoothController.init():55 - peripheral:didDiscoverCharacteristicsForService:error: - 89CF787A-F177-45F6-BE49-0F3DD92D7E20 181D (null)
10:40:28.832 🔵DEBUG BluetoothController.init():55 - Characteristics=@[2A9D]
10:40:28.833 🔵DEBUG BluetoothController.init():55 - 89CF787A-F177-45F6-BE49-0F3DD92D7E20 setNotifyValue:YES forCharacteristic:2A9D
10:40:28.861 🔵DEBUG BluetoothController.init():55 - peripheral:didUpdateNotificationStateForCharacteristic:error: - 89CF787A-F177-45F6-BE49-0F3DD92D7E20 2A9D (null)
10:40:28.862 🔵DEBUG BluetoothController.init():55 - Notify=YES
10:40:40.758 🔵DEBUG BluetoothController.init():55 - peripheral:didUpdateValueForCharacteristic:error: - 89CF787A-F177-45F6-BE49-0F3DD92D7E20 2A9D (null)
10:40:40.759 🔵DEBUG BluetoothController.init():55 - Value=<205050>
10:40:45.509 🔵DEBUG BluetoothController.init():55 - centralManager:didDisconnectPeripheral:error: - <CBCentralManager: 0x1720616c0> 89CF787A-F177-45F6-BE49-0F3DD92D7E20 Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us." UserInfo={NSLocalizedDescription=The specified device has disconnected from us.}
10:40:45.512 🔵DEBUG BluetoothController.init():55 - connectPeripheral:89CF787A-F177-45F6-BE49-0F3DD92D7E20 options:(null)
10:40:51.601 🔵DEBUG BluetoothController.init():55 - centralManager:didConnectPeripheral: - <CBCentralManager: 0x1720616c0> 89CF787A-F177-45F6-BE49-0F3DD92D7E20

RZBCentralManager state is always unknown

Whenever I access state it always reports it as unknown. If I launch the app with Bluetooth powered off, the system provided alert to turn on Bluetooth does not turn on.

I am initializing the central manager as follows
manager = RZBCentralManager(identifier: "myrestorationidentifier", peripheralClass: Device.self, queue: DispatchQueue.main)

Maybe I'm missing something...

EDIT:

If I set the centralStateHandler I get the correct state from the state property.

Connection Event shouldn't be called when cancelling connection from within the app

Issue

I am trying to distinguish two cases:

  1. The peripheral disconnects because the connection broke or it was otherwise disconnected with the device controls
  2. The peripheral disconnects due to a user action or automatic action within the app

Previously, using another library (RKBluetooth), the peripheral connection had a callback block for completion of connection and for disconnection. The disconnection block would only be called when the device disconnected by itself.

I am missing this feature on RZBluetooth, maybe I just didn't discover it yet. I don't understand the relationship between the callback block of the connection or cancel connection methods of the RZPeripheral, and the connectionEvent delegate callback.
Both are executed, while the method callback blocks are executed before the delegate method.

I wonder what the preferred way of handling this distinction is? I don't want to store a flag when the method callback block is called so that I can prevent the connectionEvent to execute, which is the only workaround I can think of right now.

Unit tests crash using Xcode 9.2/macOS 10.12.6

Issue

1.2 Unit tests that passed successfully in the past (at least as of June 2017, Xcode 8.3.3) are now crashing consistently when run under Xcode 9.2 and macOS Sierra 10.12.6.

The crash is occurring in RZBSimulatedCallback.m, line 118, in the -cancel method.

Unit Test Results

Test Suite 'SwiftTestCase' passed at 2018-04-06 16:47:43.958.
	 Executed 2 tests, with 0 failures (0 unexpected) in 0.318 (0.320) seconds
Test Suite 'RZBluetoothTests.xctest' failed at 2018-04-06 16:47:43.959.
	 Executed 66 tests, with 3 failures (0 unexpected) in 1.825 (1.836) seconds
Test Suite 'Selected tests' failed at 2018-04-06 16:47:43.960.
	 Executed 66 tests, with 3 failures (0 unexpected) in 1.825 (1.838) seconds
Failing tests:
	-[RZBSimulatedTests testConnectionAndCancelWhileNotConnectable]
	-[RZBSimulatedTests testStateBounce]
	-[RZBSimulationObjectTests testSimulatedCallbackPaused]
** TEST FAILED **

Stack Trace

Process:               xctest [67675]
Path:                  /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Agents/xctest
Identifier:            xctest
Version:               13764
Code Type:             X86-64 (Native)
Parent Process:        launchd_sim [53442]
Responsible:           xctest [67675]
User ID:               501

Date/Time:             2018-04-06 16:47:22.602 -0400
OS Version:            Mac OS X 10.12.6 (16G1314)
Report Version:        12
Anonymous UUID:        AA94F0D2-7CE4-3EC1-8A8F-AE5F930B5E34

Sleep/Wake UUID:       F138C980-C2C9-49DB-9B98-4110F6016067

Time Awake Since Boot: 150000 seconds
Time Since Wake:       5300 seconds

System Integrity Protection: enabled

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes:       0x0000000000000001, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Illegal instruction: 4
Termination Reason:    Namespace SIGNAL, Code 0x4
Terminating Process:   exc handler [0]

Application Specific Information:
BUG IN CLIENT OF LIBDISPATCH: Release of an inactive object
CoreSimulator 494.33 - Device: iPhone 7 - Runtime: iOS 11.2 (15C107) - DeviceType: iPhone 7

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libdispatch.dylib             	0x0000000115def80a _dispatch_queue_xref_dispose + 73
1   libdispatch.dylib             	0x0000000115dece2a -[OS_dispatch_source _xref_dispose] + 17
2   com.apple.CoreFoundation      	0x000000010f31361d -[__NSArrayM removeAllObjects] + 237
3   com.raizlabs.RZBluetoothTests 	0x0000000124875828 -[RZBSimulatedCallback cancel] + 696 (RZBSimulatedCallback.m:118)
4   com.raizlabs.RZBluetoothTests 	0x0000000124877911 -[RZBSimulatedConnection reset] + 369 (RZBSimulatedConnection.m:101)
5   com.raizlabs.RZBluetoothTests 	0x000000012485c41a -[RZBSimulatedTests testStateBounce] + 4282 (RZBSimulatedTests.m:222)
6   com.apple.CoreFoundation      	0x000000010f2f136c __invoking___ + 140
7   com.apple.CoreFoundation      	0x000000010f2f1240 -[NSInvocation invoke] + 320
8   com.apple.dt.XCTest           	0x000000010e8d5e30 __24-[XCTestCase invokeTest]_block_invoke + 591
9   com.apple.dt.XCTest           	0x000000010e91f17e -[XCUITestContext performInScope:] + 183
10  com.apple.dt.XCTest           	0x000000010e8d5bd6 -[XCTestCase invokeTest] + 141
11  com.apple.dt.XCTest           	0x000000010e8d6b97 __26-[XCTestCase performTest:]_block_invoke.369 + 42
12  com.apple.dt.XCTest           	0x000000010e923f25 +[XCTContext runInContextForTestCase:block:] + 163
13  com.apple.dt.XCTest           	0x000000010e8d6533 -[XCTestCase performTest:] + 608
14  com.apple.dt.XCTest           	0x000000010e8d2539 __27-[XCTestSuite performTest:]_block_invoke + 363
15  com.apple.dt.XCTest           	0x000000010e8d1ea0 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 26
16  com.apple.dt.XCTest           	0x000000010e8d209d -[XCTestSuite performTest:] + 239
17  com.apple.dt.XCTest           	0x000000010e8d2539 __27-[XCTestSuite performTest:]_block_invoke + 363
18  com.apple.dt.XCTest           	0x000000010e8d1ea0 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 26
19  com.apple.dt.XCTest           	0x000000010e8d209d -[XCTestSuite performTest:] + 239
20  com.apple.dt.XCTest           	0x000000010e8d2539 __27-[XCTestSuite performTest:]_block_invoke + 363
21  com.apple.dt.XCTest           	0x000000010e8d1ea0 -[XCTestSuite _performProtectedSectionForTest:testSection:] + 26
22  com.apple.dt.XCTest           	0x000000010e8d209d -[XCTestSuite performTest:] + 239
23  com.apple.dt.XCTest           	0x000000010e92b64f __44-[XCTTestRunSession runTestsAndReturnError:]_block_invoke + 40
24  com.apple.dt.XCTest           	0x000000010e8e571a -[XCTestObservationCenter _observeTestExecutionForBlock:] + 475
25  com.apple.dt.XCTest           	0x000000010e92b4ee -[XCTTestRunSession runTestsAndReturnError:] + 281
26  com.apple.dt.XCTest           	0x000000010e8c1af1 -[XCTestDriver runTestsAndReturnError:] + 314
27  com.apple.dt.XCTest           	0x000000010e923190 _XCTestMain + 619
28  xctest                        	0x000000010dfcc591 0x10dfca000 + 9617
29  libdyld.dylib                 	0x0000000115e61d81 start + 1

Carthage Support

Issue

Carthage is a better and non invasive way to manage dependencies, it should be added.

Remove RZBBluetoothRepresentable

RZBBluetoothRepresentable was an approach to simplify serialization, but I don't think it helps very much and it adds service to the API that makes it more complex. The Device Information profile API will need to be changed to accommodate this.

RZBPeripheral and RZBSimulatedDevice cannot handle same characteristic UUID on multiple services

Issue

The RZBSimulatedDevice class cannot handle the case where a device implements 2 or more services that share a common characteristic UUID. If you try to add a read/write/subscribe handler for the duplicate characteristic UUID, obviously, it overwrites the previous one added.

The internal readHandlers, writeHandlers, and subscribeHandlers dictionaries use only the characteristic UUID as the key; they should use a combination of characteristic UUID and service UUID (or an RZBUUIDPath).

Similarly, the corresponding {add|remove}{Read|Write|Subscribe}CallbackForCharacteristicUUID:handler: methods should be modified to take an additional service UUID parameter (or replace them with an RZBUUIDPath parameter).

This is probably not a common use case, but it can happen, for instance, if a device implements a Record Access Control Point for 2 different kinds of data records on separate services.

Crash when testing...

I'm using the library in an XCTest, and consistently crash here. It's pretty bizarre; centralManager seems to be a RZBCentralManager (see debug console), but the assert fails anyway...

screen shot 2016-01-15 at 15 23 41

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.