Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New notifications observing logic, Peripheral provider and unit tests. #215

Merged
merged 6 commits into from
Mar 13, 2018

Conversation

minixT
Copy link
Collaborator

@minixT minixT commented Mar 5, 2018

Part of #173 and #207

A few changes that update API:

  • added PeripheralProvider - central place constructing and managing Peripheral objects
  • added CharacteristicNotificationManager - which is used to observe characteristics value update and change notification flags on characteristics
  • discovering services, includedServices and characteristics will emit RxError.noElements error, if not all objects with requested UUIDs were discovered

Added unit tests to the missing classes.

Copy link

@rohitjb rohitjb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is huge, it would be great if we make small PRs and that will give a great context to us.

Copy link
Contributor

@pouljohn1 pouljohn1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! tests are awesome! Need only some changes due to thread safe issues


Notification is automaticaly unregistered once this observable is unsubscribed

- returns: `Observable` emitting `Next` with `Characteristic` when the notification setup is complete.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be change to

- returns: `Observable` emitting `Next` with `Characteristic` when give characteristic has been changed.


class CharacteristicNotificationManager {

private unowned let peripheral: CBPeripheral
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add unowned to Peripheral.manager? Now CentralManager has strong reference to PeripheralProvider which has strong reference to Peripheral. In that case there is reference cycle, so we need to add unowned to Peripheral.manager

.do(onDispose: { [weak self] in
guard let strongSelf = self else { return }
do { strongSelf.lock.lock(); defer { strongSelf.lock.unlock() }
strongSelf.uuidToActiveObservableMap.removeValue(forKey: characteristic.uuid)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am just thinking of situation when:

  1. Thread 1 stops at line 53
  2. Thread 2 locks at line 42 and taking observable on line 44-45
  3. Thread 1 removing observable from map at line 55
    In this screnario Thread 2 receives observable that is disposed. Let's talk about this when you will be at work

do { strongSelf.lock.lock(); defer { strongSelf.lock.unlock() }
strongSelf.uuidToActiveObservableMap.removeValue(forKey: characteristic.uuid)
}
_ = strongSelf.setNotifyValue(false, for: characteristic)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we would not need to wait here for CBPeripheralDelegate peripheral:didUpdateNotificationStateForCharacteristic:error:
There could be scenario like:

  1. Thread 1 stops on line 57 (setNotifyValue not called)
  2. Thread 2 goes to line 62, subscibes for observable and calls setNotifyValue(true)
  3. Thread 1 is doing setNotifyValue(false) on line 57
    In result we have setNotifyValue turned to false and existing observable for notification change (which will never receive notification)

Simple solution would be to wrap this line in do lock from line 54. In that case it would be not possible to call setNotifyValue with false after setNotifyValue with true. Question here is, how will CoreBluetooth behave when we will set 2x setNotifyValue one after another

Notification is automaticaly unregistered once this observable is unsubscribed

- parameter characteristic: `Characteristic` for notification setup.
- returns: `Observable` emitting `Peripheral` when the notification setup is complete.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here for doc

Notification is automaticaly unregistered once this observable is unsubscribed

- parameter characteristic: `Characteristic` for notification setup.
- returns: `Observable` emitting `Peripheral` when the notification setup is complete.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

peripheralsBox.compareAndSet(
compare: { peripherals in
return !peripherals.contains(where: { $0.peripheral == cbPeripheral })
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add tab here

@pouljohn1
Copy link
Contributor

@rohitjb agree that we could split doing tests to other PR, but due to big delays in reviewing we wanted to make it in one shoot. It won't happen again in future ;)

Copy link
Contributor

@pouljohn1 pouljohn1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One additional catch ;)

return .deferred { [weak self] in
if enabled {
let counter = self?.uuidToActiveObservablesCountMap[characteristic.uuid] ?? 0
self?.uuidToActiveObservablesCountMap[characteristic.uuid] = counter + 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that it should be also guarded by NSLock.
Imagine scenario:

  1. thread 1 is on line 89, after getting counter value (value==1)
  2. thread 2 goes to line 58 and decrease counter (value==0)
  3. thread 1 changes coun map on line 89 (value==2)
    Finally we have count==2 and only 1 observable.

Copy link
Contributor

@pouljohn1 pouljohn1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like it ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants