Skip to content

Commit

Permalink
v0.8.2 - Add initial support for did modify services (#193)
Browse files Browse the repository at this point in the history
* Add initial support for did modify services delegation, #144

* Bump version to 0.8.2

* Update changelog for 0.8.2

* Update readme and jazzy doc for 0.8.2
  • Loading branch information
Jeremy Chiang authored Mar 21, 2019
1 parent 320dc60 commit 3c8908b
Show file tree
Hide file tree
Showing 116 changed files with 1,532 additions and 136 deletions.
4 changes: 2 additions & 2 deletions Bluejay.podspec
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = 'Bluejay'
spec.version = '0.8.1'
spec.version = '0.8.2'
spec.license = { type: 'MIT', file: 'LICENSE' }
spec.homepage = 'https://github.com/steamclock/bluejay'
spec.authors = { 'Jeremy Chiang' => '[email protected]' }
spec.summary = 'Bluejay is a simple Swift framework for building reliable Bluetooth apps.'
spec.homepage = 'https://github.com/steamclock/bluejay'
spec.source = { git: 'https://github.com/steamclock/bluejay.git', tag: 'v0.8.1' }
spec.source = { git: 'https://github.com/steamclock/bluejay.git', tag: 'v0.8.2' }
spec.source_files = 'Bluejay/Bluejay/*.{h,swift}'
spec.framework = 'SystemConfiguration'
spec.platform = :ios, '10.0'
Expand Down
2 changes: 1 addition & 1 deletion Bluejay/.jazzy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ output: ../docs
author: Steamclock Software
author_url: http://steamclock.com
module: Bluejay
module_version: 0.8.1
module_version: 0.8.2
readme: ../README.md
sdk: iphone
copyright: Copyright © 2017 Steamclock Software. All rights reserved.
Expand Down
4 changes: 4 additions & 0 deletions Bluejay/Bluejay.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
B8383B7221C3239B00F07306 /* PeripheralDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8383B7121C3239B00F07306 /* PeripheralDelegate.swift */; };
B83A67CE219F45C300076B9F /* BackgroundRestorer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83A67CD219F45C300076B9F /* BackgroundRestorer.swift */; };
B842AAF81E4CF74300BB32EE /* String+Transferable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B842AAF71E4CF74300BB32EE /* String+Transferable.swift */; };
B843657E221F73FC00990C83 /* ServiceObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B843657D221F73FC00990C83 /* ServiceObserver.swift */; };
B86586A9216D1105002E8E2D /* StartOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86586A8216D1105002E8E2D /* StartOptions.swift */; };
B869A2971E721D50003C1278 /* Data+Sendable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B869A2961E721D50003C1278 /* Data+Sendable.swift */; };
B87CBB4D21C05F8400B67B5B /* BackgroundRestoreConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = B87CBB4C21C05F8400B67B5B /* BackgroundRestoreConfig.swift */; };
Expand Down Expand Up @@ -152,6 +153,7 @@
B8383B7121C3239B00F07306 /* PeripheralDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralDelegate.swift; sourceTree = "<group>"; };
B83A67CD219F45C300076B9F /* BackgroundRestorer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundRestorer.swift; sourceTree = "<group>"; };
B842AAF71E4CF74300BB32EE /* String+Transferable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Transferable.swift"; sourceTree = "<group>"; };
B843657D221F73FC00990C83 /* ServiceObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceObserver.swift; sourceTree = "<group>"; };
B86586A8216D1105002E8E2D /* StartOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartOptions.swift; sourceTree = "<group>"; };
B869A2961E721D50003C1278 /* Data+Sendable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Sendable.swift"; sourceTree = "<group>"; };
B87CBB4C21C05F8400B67B5B /* BackgroundRestoreConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundRestoreConfig.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -460,6 +462,7 @@
children = (
B8C70FB81E1C725C0006CF58 /* ConnectionObserver.swift */,
B8FE9DF41E73597A00D361CE /* RSSIObserver.swift */,
B843657D221F73FC00990C83 /* ServiceObserver.swift */,
B8DC6C6D21DD7917004A8EA6 /* LogObserver.swift */,
B80D299A2170062D001C3C9B /* DisconnectHandler.swift */,
);
Expand Down Expand Up @@ -819,6 +822,7 @@
B8383B7021C31B2D00F07306 /* ListenRestorer.swift in Sources */,
B8022D9C1E1F052F00EA360B /* ListenAction.swift in Sources */,
B8C70F9B1E1C5A910006CF58 /* CharacteristicIdentifier.swift in Sources */,
B843657E221F73FC00990C83 /* ServiceObserver.swift in Sources */,
B869A2971E721D50003C1278 /* Data+Sendable.swift in Sources */,
B8D2078C1E26D446007E670A /* CBPeripheralState+ReturnString.swift in Sources */,
B88F79951EB7DB420094D8D1 /* Operation.swift in Sources */,
Expand Down
31 changes: 31 additions & 0 deletions Bluejay/Bluejay/Bluejay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public class Bluejay: NSObject { //swiftlint:disable:this type_body_length
/// List of weak references to objects interested in receiving notifications on RSSI reads.
private var rssiObservers: [WeakRSSIObserver] = []

/// List of weak references to objects interested in receiving notifications on services changes.
private var serviceObservers: [WeakServiceObserver] = []

/// List of weak references to objects interested in receiving notifications on log file changes.
private var logObservers: [WeakLogObserver] = []

Expand Down Expand Up @@ -496,6 +499,25 @@ public class Bluejay: NSObject { //swiftlint:disable:this type_body_length
rssiObservers = rssiObservers.filter { $0.weakReference != nil && $0.weakReference !== rssiObserver }
}

/**
Register for notifications when a connected peripheral's services change. Unregistering is not required, Bluejay will unregister for you if the observer is no longer in memory.
- Parameter serviceObserver: object interested in receiving the connected peripheral's did modify services event.
*/
public func register(serviceObserver: ServiceObserver) {
serviceObservers = serviceObservers.filter { $0.weakReference != nil && $0.weakReference !== serviceObserver }
serviceObservers.append(WeakServiceObserver(weakReference: serviceObserver))
}

/**
Unregister for notifications when a connected peripheral's services change. Unregistering is not required, Bluejay will unregister for you if the observer is no longer in memory.
- Parameter serviceObserver: object no longer interested in receiving the connected peripheral's did modify services event.
*/
public func unregister(serviceObserver: ServiceObserver) {
serviceObservers = serviceObservers.filter { $0.weakReference != nil && $0.weakReference !== serviceObserver }
}

/**
Register for notifications when the log file is updated. Unregistering is not required, Bluejay will unregister for you if the observer is no longer in memory.
Expand Down Expand Up @@ -1535,6 +1557,15 @@ extension Bluejay: PeripheralDelegate {
observer.weakReference?.didReadRSSI(from: peripheral.identifier, RSSI: RSSI, error: error)
}
}

func didModifyServices(from peripheral: Peripheral, invalidatedServices: [ServiceIdentifier]) {
for observer in serviceObservers {
observer.weakReference?.didModifyServices(
from: peripheral.identifier,
invalidatedServices: invalidatedServices
)
}
}
}

let logger = XCGLogger(identifier: "Bluejay", includeDefaultDestinations: true)
Expand Down
2 changes: 1 addition & 1 deletion Bluejay/Bluejay/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.8.0</string>
<string>0.8.2</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
10 changes: 10 additions & 0 deletions Bluejay/Bluejay/Peripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -333,4 +333,14 @@ extension Peripheral: CBPeripheralDelegate {
delegate.didReadRSSI(from: self, RSSI: RSSI, error: error)
}

/// Called when the peripheral removed or added services.
public func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
delegate.didModifyServices(
from: self,
invalidatedServices: invalidatedServices.map {
ServiceIdentifier(uuid: $0.uuid)
}
)
}

}
3 changes: 3 additions & 0 deletions Bluejay/Bluejay/PeripheralDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ protocol PeripheralDelegate: class {

/// The peripheral has received a RSSI value and notifies Bluejay.
func didReadRSSI(from peripheral: Peripheral, RSSI: NSNumber, error: Error?)

/// The peripheral's list of available services has changed.
func didModifyServices(from peripheral: Peripheral, invalidatedServices: [ServiceIdentifier])
}
28 changes: 28 additions & 0 deletions Bluejay/Bluejay/ServiceObserver.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// ServiceObserver.swift
// Bluejay
//
// Created by Jeremy Chiang on 2019-02-21.
// Copyright © 2019 Steamclock Software. All rights reserved.
//

import Foundation

/**
A protocol allowing conforming objects to monitor the services changes of a connected peripheral.
*/
public protocol ServiceObserver: class {
/**
* Called whenever a peripheral's services change.
*
* - Parameters:
* - from: the peripheral that changed services.
* - invalidatedServices: the services invalidated.
*/
func didModifyServices(from peripheral: PeripheralIdentifier, invalidatedServices: [ServiceIdentifier])
}

/// Allows creating weak references to ServiceObserver objects, so that Bluejay does not keep strong references to observers and prevent them from being released in memory.
struct WeakServiceObserver {
weak var weakReference: ServiceObserver?
}
15 changes: 15 additions & 0 deletions Bluejay/BluejayHeartSensorDemo/SensorViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class SensorViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
bluejay.register(connectionObserver: self)
bluejay.register(serviceObserver: self)
}

override func willMove(toParent parent: UIViewController?) {
Expand Down Expand Up @@ -228,3 +229,17 @@ extension SensorViewController: ConnectionObserver {
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
}

extension SensorViewController: ServiceObserver {
func didModifyServices(from peripheral: PeripheralIdentifier, invalidatedServices: [ServiceIdentifier]) {
bluejay.log("SensorViewController - Invalidated services: \(invalidatedServices.debugDescription)")

if invalidatedServices.contains(where: { invalidatedServiceIdentifier -> Bool in
invalidatedServiceIdentifier == chirpCharacteristic.service
}) {
endListen(to: chirpCharacteristic)
} else if invalidatedServices.isEmpty {
listen(to: chirpCharacteristic)
}
}
}
106 changes: 84 additions & 22 deletions Bluejay/DittojayHeartSensorDemo/DittojayViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class DittojayViewController: UITableViewController {
var addedServices: [CBService] = []

var heartRate: UInt8 = 0
var timer: Timer?

override func viewDidLoad() {
super.viewDidLoad()
Expand All @@ -48,6 +49,12 @@ class DittojayViewController: UITableViewController {
let wakeAppServiceUUID = CBUUID(string: "CED261B7-F120-41C8-9A92-A41DE69CF2A8")
let wakeAppCharacteristicUUID = CBUUID(string: "83B4A431-A6F1-4540-B3EE-3C14AEF71A04")

if addedServices.contains(where: { addedService -> Bool in
addedService.uuid == wakeAppServiceUUID
}) {
return
}

wakeAppCharacteristic = CBMutableCharacteristic(
type: wakeAppCharacteristicUUID,
properties: .notify,
Expand All @@ -57,15 +64,35 @@ class DittojayViewController: UITableViewController {
wakeAppService = CBMutableService(type: wakeAppServiceUUID, primary: true)
wakeAppService.characteristics = [wakeAppCharacteristic]

debugPrint("Will add wake app service...")

manager.add(wakeAppService)
}

private func removeWakeAppService() {
addedServices.removeAll { addedService -> Bool in
addedService.uuid == wakeAppService.uuid
}

debugPrint("Will remove wake app service...")

manager.remove(wakeAppService)

if manager.isAdvertising {
debugPrint("Will stop advertising...")
manager.stopAdvertising()
}

debugPrint("Will start advertising...")
advertiseServices([heartRateService.uuid])
}

private func advertiseServices(_ services: [CBUUID]) {
manager.startAdvertising([CBAdvertisementDataServiceUUIDsKey: services])
}

private func startHeartRateSensor() {
_ = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
guard let weakSelf = self else {
return
}
Expand Down Expand Up @@ -93,7 +120,7 @@ class DittojayViewController: UITableViewController {
}

override func numberOfSections(in tableView: UITableView) -> Int {
return 1
return 2
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
Expand All @@ -103,32 +130,54 @@ class DittojayViewController: UITableViewController {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "actionCell", for: indexPath)

if indexPath.row == 0 {
cell.textLabel?.text = "Generated Heart Rate"
cell.detailTextLabel?.text = "\(heartRate)"

DispatchQueue.main.async {
UIView.animate(withDuration: 0.25, animations: {
cell.detailTextLabel?.transform = cell.detailTextLabel!.transform.scaledBy(x: 1.5, y: 1.5)
}, completion: { completed in
if completed {
UIView.animate(withDuration: 0.25) {
cell.detailTextLabel?.transform = CGAffineTransform.identity
if indexPath.section == 0 {
if indexPath.row == 0 {
cell.textLabel?.text = "Generated Heart Rate"
cell.detailTextLabel?.text = "\(heartRate)"
cell.selectionStyle = .none

DispatchQueue.main.async {
UIView.animate(withDuration: 0.25, animations: {
cell.detailTextLabel?.transform = cell.detailTextLabel!.transform.scaledBy(x: 1.5, y: 1.5)
}, completion: { completed in
if completed {
UIView.animate(withDuration: 0.25) {
cell.detailTextLabel?.transform = CGAffineTransform.identity
}
}
}
})
})
}
} else {
cell.textLabel?.text = "Chirp"
cell.detailTextLabel?.text = ""
}
} else {
cell.textLabel?.text = "Chirp"
cell.detailTextLabel?.text = ""
if indexPath.row == 0 {
cell.textLabel?.text = "Add Chirp Service"
cell.detailTextLabel?.text = ""
} else {
cell.textLabel?.text = "Remove Chirp Service"
cell.detailTextLabel?.text = ""
}
}

return cell
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
chirp()

if indexPath.section == 0 {
if indexPath.row == 1 {
chirp()
}
} else {
if indexPath.row == 0 {
addWakeAppService()
} else {
removeWakeAppService()
}
}
}
}

Expand All @@ -146,13 +195,26 @@ extension DittojayViewController: CBPeripheralManagerDelegate {
if let error = error {
debugPrint("Failed to add service \(service.uuid.uuidString) with error: \(error.localizedDescription)")
} else {
debugPrint("Added service \(service.uuid.uuidString)")

addedServices.append(service)
if !addedServices.contains { addedService -> Bool in
addedService.uuid == service.uuid
} {
debugPrint("Added service \(service.uuid.uuidString)")
addedServices.append(service)
}

if addedServices.contains(heartRateService) && addedServices.contains(wakeAppService) {
if manager.isAdvertising {
debugPrint("Will stop advertising...")
manager.stopAdvertising()
}

debugPrint("Will start advertising...")
advertiseServices([heartRateService.uuid, wakeAppService.uuid])
startHeartRateSensor()

if timer == nil {
debugPrint("Will start heart rate sensor...")
startHeartRateSensor()
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
### Changed

## [0.8.2] - 2019-02-25
### Added
- Added initial support for did modify services delegation

## [0.8.1] - 2019-01-14
### Fixed
- Fixed and improved Carthage instruction
Expand Down
Loading

0 comments on commit 3c8908b

Please sign in to comment.