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

Support retrieving known Peripherals #38

Merged
merged 59 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
a8cbccb
Add accessory discovery criteria
Supereg Jun 12, 2024
603d97f
Support retrieving paired peripherals
Supereg Jun 13, 2024
d97c718
Treat retrieved peripherals as observed peripherals for now
Supereg Jun 13, 2024
65c5085
Compiles
Supereg Jun 13, 2024
b6aee6c
Use new Timeout Error and withTimeout infrastructure
Supereg Jun 14, 2024
ed51fb0
Release
Supereg Jun 18, 2024
262f1d6
Change behavior of `name`, make `localName` public and add support fo…
Supereg Jun 18, 2024
07aa4ff
Add missing keypath translations
Supereg Jun 18, 2024
b507919
Make sure CharacteristicAccess is not accidentally consumed
Supereg Jun 19, 2024
0e712a8
Support retrieving paired devices.
Supereg Jun 19, 2024
8a84ae2
Minor fixes
Supereg Jun 20, 2024
9a8ce60
Swiftlint fix
Supereg Jun 20, 2024
09ab405
Forward device description to retrieve peripheral
Supereg Jun 20, 2024
6c41d14
Make init public
Supereg Jun 21, 2024
0e2118c
Fix compilation
Supereg Jun 21, 2024
118b730
Fix
Supereg Jun 21, 2024
61cc668
Make Bluetooth module configuration public.
Supereg Jun 21, 2024
9d46389
Make it explicitly nonisolated
Supereg Jun 21, 2024
480adfd
Some progress
Supereg Jun 21, 2024
0b8b45f
Move BluetoothViews to SpeziDevicesUI, rename BluetoothServices to Sp…
Supereg Jun 21, 2024
fcb50b9
Adjust TestApp
Supereg Jun 22, 2024
b4d6df2
Merge branch 'main' into feature/accessory-discovery
Supereg Jun 22, 2024
9d62f3b
Refactor discovery state into DiscoverySession. Support dynamic confi…
Supereg Jun 22, 2024
39a777f
Final design for persistent devices
Supereg Jun 22, 2024
2f3bd4e
Docs and other refinements
Supereg Jun 22, 2024
722f00f
Fully implement how BluetoothDevice reference and retrieved devices a…
Supereg Jun 23, 2024
81734bd
Support retrieiving all connected devices from the environment
Supereg Jun 23, 2024
a618b79
Skip macro and plugin validation in custom command
Supereg Jun 23, 2024
7912da3
Just use a generic name for easier testing
Supereg Jun 23, 2024
fa5365f
Fix crash when refreshing view
Supereg Jun 23, 2024
21a245a
Implement testing for retrieiving peripherals and ensure peripherals …
Supereg Jun 24, 2024
9f78763
Remove forgotten print statements
Supereg Jun 24, 2024
48417d0
Minor changes
Supereg Jun 24, 2024
194e2c3
Add Codable conformance for all Characteristics
Supereg Jun 25, 2024
9497780
Make Units RawRepresentable and fix swiftlint
Supereg Jun 25, 2024
394c1fd
Add support for receiving the previous value as well
Supereg Jun 25, 2024
41f2ce4
Merge scanning options from the environment
Supereg Jun 25, 2024
b82f3aa
Support supplying Bluetooth options via the environment
Supereg Jun 25, 2024
9644307
Docs
Supereg Jun 25, 2024
4ba8359
Update docs catalog
Supereg Jun 25, 2024
7c91e9e
Update language around power and permission alerts
Supereg Jun 26, 2024
9697b2a
Enable StrictConcurrency and make SwiftLint not enable by default
Supereg Jun 26, 2024
b07a47a
Remove unused code
Supereg Jun 26, 2024
1745d29
Fix test compiling
Supereg Jun 26, 2024
b10ca56
Support more extensive device state and characteristic simulation
Supereg Jun 26, 2024
442fc0d
Update to Spezi release
Supereg Jun 26, 2024
9f281ec
Make init public
Supereg Jun 26, 2024
c9b59ce
docs
Supereg Jun 26, 2024
bad334b
Remove code that is already present in SpeziNetworking
Supereg Jun 26, 2024
6adffbe
Fix import
Supereg Jun 26, 2024
c609478
Use XCTestExtensions preview
Supereg Jun 27, 2024
63e8e61
swiftlint
Supereg Jun 27, 2024
0664502
Declare xctestextensions as explicit dependency
Supereg Jun 27, 2024
1447d5c
fix
Supereg Jun 27, 2024
94b13ee
Fix2
Supereg Jun 27, 2024
b45714c
Add ability to disable autoread
Supereg Jun 27, 2024
b93d32a
test something
Supereg Jun 27, 2024
d9df9b9
Release version
Supereg Jun 27, 2024
c23f507
revert
Supereg Jun 27, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
path: 'Tests/UITests'
artifactname: TestApp-macOS.xcresult
resultBundle: TestApp-macOS.xcresult
customcommand: "set -o pipefail && xcodebuild test -scheme 'TestApp' -configuration 'Test' -destination 'platform=macOS,arch=arm64,variant=Mac Catalyst' -derivedDataPath '.derivedData' -resultBundlePath 'TestApp-macOS.xcresult' | xcpretty"
customcommand: "set -o pipefail && xcodebuild test -scheme 'TestApp' -configuration 'Test' -destination 'platform=macOS,arch=arm64,variant=Mac Catalyst' -derivedDataPath '.derivedData' -resultBundlePath 'TestApp-macOS.xcresult' -skipPackagePluginValidation -skipMacroValidation | xcpretty"
secrets: inherit
uploadcoveragereport:
name: Upload Coverage Report
Expand Down
81 changes: 58 additions & 23 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@
// SPDX-License-Identifier: MIT
//

import class Foundation.ProcessInfo
import PackageDescription


#if swift(<6)
let swiftConcurrency: SwiftSetting = .enableExperimentalFeature("SwiftConcurrency")
#else
let swiftConcurrency: SwiftSetting = .enableUpcomingFeature("SwiftConcurrency")
#endif


let package = Package(
name: "SpeziBluetooth",
defaultLocalization: "en",
Expand All @@ -20,18 +28,17 @@ let package = Package(
.macOS(.v14)
],
products: [
.library(name: "BluetoothServices", targets: ["BluetoothServices"]),
.library(name: "BluetoothViews", targets: ["BluetoothViews"]),
.library(name: "SpeziBluetoothServices", targets: ["SpeziBluetoothServices"]),
.library(name: "SpeziBluetooth", targets: ["SpeziBluetooth"])
],
dependencies: [
.package(url: "https://github.com/StanfordSpezi/SpeziFoundation", from: "1.0.4"),
.package(url: "https://github.com/StanfordSpezi/Spezi", from: "1.3.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziNetworking", from: "2.0.1"),
.package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.3.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziFoundation", from: "1.1.0"),
.package(url: "https://github.com/StanfordSpezi/Spezi", from: "1.4.0"),
.package(url: "https://github.com/StanfordSpezi/SpeziNetworking", from: "2.1.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.59.0"),
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.4")
],
.package(url: "https://github.com/apple/swift-collections.git", from: "1.0.4"),
.package(url: "https://github.com/StanfordBDHG/XCTestExtensions.git", branch: "feature/xctassert-throws-async")
Supereg marked this conversation as resolved.
Show resolved Hide resolved
] + swiftLintPackage(),
targets: [
.target(
name: "SpeziBluetooth",
Expand All @@ -44,39 +51,67 @@ let package = Package(
],
resources: [
.process("Resources")
]
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
),
.target(
name: "BluetoothServices",
name: "SpeziBluetoothServices",
dependencies: [
.target(name: "SpeziBluetooth"),
.product(name: "ByteCoding", package: "SpeziNetworking"),
.product(name: "SpeziNumerics", package: "SpeziNetworking")
]
),
.target(
name: "BluetoothViews",
dependencies: [
.target(name: "SpeziBluetooth"),
.product(name: "SpeziViews", package: "SpeziViews")
]
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
),
.executableTarget(
name: "TestPeripheral",
dependencies: [
.target(name: "SpeziBluetooth"),
.target(name: "BluetoothServices"),
.target(name: "SpeziBluetoothServices"),
.product(name: "ByteCoding", package: "SpeziNetworking")
]
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
),
.testTarget(
name: "BluetoothServicesTests",
dependencies: [
.target(name: "BluetoothServices"),
.target(name: "SpeziBluetoothServices"),
.target(name: "SpeziBluetooth"),
.product(name: "XCTByteCoding", package: "SpeziNetworking"),
.product(name: "NIO", package: "swift-nio")
]
.product(name: "NIO", package: "swift-nio"),
.product(name: "XCTestExtensions", package: "XCTestExtensions")
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
)
]
)


func swiftLintPlugin() -> [Target.PluginUsage] {
// Fully quit Xcode and open again with `open --env SPEZI_DEVELOPMENT_SWIFTLINT /Applications/Xcode.app`
if ProcessInfo.processInfo.environment["SPEZI_DEVELOPMENT_SWIFTLINT"] != nil {
[.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint")]
} else {
[]
}
}

func swiftLintPackage() -> [PackageDescription.Package.Dependency] {
if ProcessInfo.processInfo.environment["SPEZI_DEVELOPMENT_SWIFTLINT"] != nil {
[.package(url: "https://github.com/realm/SwiftLint.git", .upToNextMinor(from: "0.55.1"))]
} else {
[]
}
}
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ class DeviceInformationService: BluetoothService {

We can use this Bluetooth service now in the `MyDevice` implementation as follows.

> Tip: We use the [`DeviceState`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/devicestate) and [`DeviceAction`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/deviceaction) property wrappers to get access to the device state and its actions. Those two
> [!TIP]
> We use the [`DeviceState`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/devicestate) and [`DeviceAction`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/deviceaction) property wrappers to get access to the device state and its actions. Those two
property wrappers can also be used within a [`BluetoothService`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/bluetoothservice) type.

```swift
Expand Down Expand Up @@ -145,15 +146,17 @@ class ExampleDelegate: SpeziAppDelegate {
Once you have the `Bluetooth` module configured within your Spezi app, you can access the module within your
[`Environment`](https://developer.apple.com/documentation/swiftui/environment).

You can use the [`scanNearbyDevices(enabled:with:autoConnect:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/swiftui/view/scanNearbyDevices(enabled:with:autoConnect:)) and [`autoConnect(enabled:with:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/swiftui/view/autoConnect(enabled:with:))
You can use the [`scanNearbyDevices(enabled:with:minimumRSSI:advertisementStaleInterval:autoConnect:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/swiftui/view/scanNearbyDevices(enabled:with:minimumRSSI:advertisementStaleInterval:autoConnect:))
and [`autoConnect(enabled:with:minimumRSSI:advertisementStaleInterval:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/swiftui/view/autoConnect(enabled:with:minimumRSSI:advertisementStaleInterval:))
modifiers to scan for nearby devices and/or auto connect to the first available device. Otherwise, you can also manually start and stop scanning for nearby devices
using [`scanNearbyDevices(autoConnect:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/bluetooth/scanNearbyDevices(autoConnect:)) and [`stopScanning()`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/bluetooth/stopScanning()).
using [`scanNearbyDevices(minimumRSSI:advertisementStaleInterval:autoConnect:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/bluetooth/scanNearbyDevices(minimumRSSI:advertisementStaleInterval:autoConnect:)) and [`stopScanning()`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/bluetooth/stopScanning()).

To retrieve the list of nearby devices you may use [`nearbyDevices(for:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/bluetooth/nearbyDevices(for:)).

> Tip: To easily access the first connected device, you can just query the SwiftUI Environment for your `BluetoothDevice` type.
Make sure to declare the property as optional using the respective [`Environment(_:)`](https://developer.apple.com/documentation/swiftui/environment/init(_:)-8slkf)
initializer.
> [!TIP]
> To easily access the first connected device, you can just query the SwiftUI Environment for your `BluetoothDevice` type.
Make sure to declare the property as optional using the respective [`Environment(_:)`](https://developer.apple.com/documentation/swiftui/environment/init(_:)-8slkf)
initializer.

The below code example demonstrates all these steps of retrieving the `Bluetooth` module from the environment, listing all nearby devices,
auto connecting to the first one and displaying some basic information of the currently connected device.
Expand Down Expand Up @@ -197,6 +200,28 @@ struct MyView: View {
}
```

> [!TIP]
> Use [`ConnectedDevices`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/ConnectedDevices) to retrieve the full list of connected devices from the SwiftUI environment.

#### Retrieving Devices

The previous section explained how to discover nearby devices and retrieve the currently connected one from the environment.
This is great ad-hoc connection establishment with devices currently nearby.
However, this might not be the most efficient approach, if you want to connect to a specific, previously paired device.
In these situations you can use the [`retrieveDevice(for:as:)`](https://swiftpackageindex.com/stanfordspezi/spezibluetooth/documentation/spezibluetooth/bluetooth/retrieveDevice(for:as:)) method to retrieve a known device.

Below is a short code example illustrating this method.

```swift
let id: UUID = ... // a Bluetooth peripheral identifier (e.g., previously retrieved when pairing the device)

let device = bluetooth.retrieveDevice(for: id, as: MyDevice.self)

await device.connect() // assume declaration of @DeviceAction(\.connect)

// Connect doesn't time out. Connection with the device will be established as soon as the device is in reach.
```

### Integration with Spezi Modules

A Spezi [`Module`](https://swiftpackageindex.com/stanfordspezi/spezi/documentation/spezi/module) is a great way of structuring your application into
Expand Down
55 changes: 0 additions & 55 deletions Sources/BluetoothServices/Characteristics/TemperatureType.swift

This file was deleted.

129 changes: 0 additions & 129 deletions Sources/BluetoothViews/BluetoothStateHint.swift

This file was deleted.

Loading
Loading