Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.9.0'
spec.license = { type: 'MIT', file: 'LICENSE' }
spec.homepage = 'https://github.com/steamclock/bluejay'
spec.authors = { 'Jeremy Chiang' => 'jeremy@steamclock.com' }
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.9.0' }
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.9.0
readme: ../README.md
sdk: iphone
copyright: Copyright © 2017 Steamclock Software. All rights reserved.
Expand Down
12 changes: 8 additions & 4 deletions Bluejay/Bluejay.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
B8022D9A1E1F041D00EA360B /* SynchronizedPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8022D991E1F041D00EA360B /* SynchronizedPeripheral.swift */; };
B8022D9C1E1F052F00EA360B /* ListenAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8022D9B1E1F052F00EA360B /* ListenAction.swift */; };
B80D299B2170062D001C3C9B /* DisconnectHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B80D299A2170062D001C3C9B /* DisconnectHandler.swift */; };
B8127C732236F63300218B97 /* FixedWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8127C722236F63300218B97 /* FixedWidth.swift */; };
B81E37731EDE238900E0655F /* DisconnectionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B81E37721EDE238900E0655F /* DisconnectionResult.swift */; };
B83271A91E313CA300D68581 /* Queueable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B83271A81E313CA300D68581 /* Queueable.swift */; };
B8330D5121B9E03B006F29C7 /* ListenCallback.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8330D5021B9E03B006F29C7 /* ListenCallback.swift */; };
Expand All @@ -24,7 +25,7 @@
B8383B7021C31B2D00F07306 /* ListenRestorer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8383B6F21C31B2D00F07306 /* ListenRestorer.swift */; };
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 @@ -138,6 +139,7 @@
B8022D991E1F041D00EA360B /* SynchronizedPeripheral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizedPeripheral.swift; sourceTree = "<group>"; };
B8022D9B1E1F052F00EA360B /* ListenAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListenAction.swift; sourceTree = "<group>"; };
B80D299A2170062D001C3C9B /* DisconnectHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectHandler.swift; sourceTree = "<group>"; };
B8127C722236F63300218B97 /* FixedWidth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixedWidth.swift; sourceTree = "<group>"; };
B81E37721EDE238900E0655F /* DisconnectionResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisconnectionResult.swift; sourceTree = "<group>"; };
B82FE820DEBA78205D73DB03 /* Pods_BluejayHeartSensorDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BluejayHeartSensorDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B83271A81E313CA300D68581 /* Queueable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queueable.swift; sourceTree = "<group>"; };
Expand All @@ -151,7 +153,7 @@
B8383B6F21C31B2D00F07306 /* ListenRestorer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListenRestorer.swift; sourceTree = "<group>"; };
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 @@ -261,8 +263,8 @@
children = (
B8C70FAA1E1C69C50006CF58 /* Sendable.swift */,
B8C70FA81E1C69B60006CF58 /* Receivable.swift */,
B8127C722236F63300218B97 /* FixedWidth.swift */,
B8C70FAE1E1C6C780006CF58 /* Integer+Transferable.swift */,
B842AAF71E4CF74300BB32EE /* String+Transferable.swift */,
B869A2961E721D50003C1278 /* Data+Sendable.swift */,
B8B5708E1ED8AEAA00603C0B /* Data+Extractable.swift */,
);
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 @@ -794,7 +797,7 @@
B8C70FA31E1C65000006CF58 /* ConnectionResult.swift in Sources */,
B88A0DA11E1DD15600A00FF8 /* DiscoverCharacteristic.swift in Sources */,
B80D299B2170062D001C3C9B /* DisconnectHandler.swift in Sources */,
B842AAF81E4CF74300BB32EE /* String+Transferable.swift in Sources */,
B8127C732236F63300218B97 /* FixedWidth.swift in Sources */,
B8DC6C6E21DD7917004A8EA6 /* LogObserver.swift in Sources */,
B83A67CE219F45C300076B9F /* BackgroundRestorer.swift in Sources */,
B83271A91E313CA300D68581 /* Queueable.swift in Sources */,
Expand All @@ -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
28 changes: 26 additions & 2 deletions Bluejay/Bluejay/Data+Extractable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,42 @@ import Foundation
extension Data {

/**
Convenience function to read a range of Data and deserialize it into the specified type.
Convenience function to read a range of Data and deserialize it into a fixed width type.

- Parameters:
- start: The starting position of the range to read.
- length: The number of bytes to read from `start`.
*/
public func extract<T>(start: Int, length: Int) throws -> T {
public func extract<T: FixedWidth>(start: Int, length: Int) throws -> T {
if start + length > self.count {
throw BluejayError.dataOutOfBounds(start: start, length: length, count: self.count)
}

return self.subdata(in: start..<start + length).withUnsafeBytes { $0.pointee }
}

/**
Convenience function to extract a range of Data.

- Parameters:
- start: The starting position of the range to read.
- length: The number of bytes to read from `start`.
*/
public func extract(start: Int, length: Int) throws -> Data {
return self.subdata(in: start..<start + length)
}

/**
Convenience function to read a range of Data and deserialize it into a String.

- Note: Defaults to using utf8 encoding.

- Parameters:
- start: The starting position of the range to read.
- length: The number of bytes to read from `start`.
- encoding: The string encoding to use, defaults to utf8.
*/
public func extract(start: Int, length: Int, encoding: String.Encoding = .utf8) throws -> String? {
return String(data: self.subdata(in: start..<start + length), encoding: encoding)
}
}
12 changes: 12 additions & 0 deletions Bluejay/Bluejay/FixedWidth.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// FixedWidth.swift
// Bluejay
//
// Created by Jeremy Chiang on 2019-03-11.
// Copyright © 2019 Steamclock Software. All rights reserved.
//

import Foundation

/// Marker protocol to help enforce extraction only on primitives or fixed width types.
public protocol FixedWidth {}
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.9.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
Expand Down
16 changes: 8 additions & 8 deletions Bluejay/Bluejay/Integer+Transferable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ extension BinaryInteger {
}

/// Extensions to existing primitive types to make them Sendable and Receivable.
extension Int64: Sendable, Receivable {}
extension Int32: Sendable, Receivable {}
extension Int16: Sendable, Receivable {}
extension Int8: Sendable, Receivable {}
extension Int64: Sendable, Receivable, FixedWidth {}
extension Int32: Sendable, Receivable, FixedWidth {}
extension Int16: Sendable, Receivable, FixedWidth {}
extension Int8: Sendable, Receivable, FixedWidth {}

extension UInt64: Sendable, Receivable {}
extension UInt32: Sendable, Receivable {}
extension UInt16: Sendable, Receivable {}
extension UInt8: Sendable, Receivable {}
extension UInt64: Sendable, FixedWidth {}
extension UInt32: Sendable, FixedWidth {}
extension UInt16: Sendable, FixedWidth {}
extension UInt8: Sendable, FixedWidth {}
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?
}
24 changes: 0 additions & 24 deletions Bluejay/Bluejay/String+Transferable.swift

This file was deleted.

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)
}
}
}
Loading