Skip to content

Commit

Permalink
Move BluetoothViews to SpeziDevices
Browse files Browse the repository at this point in the history
  • Loading branch information
Supereg committed Jun 21, 2024
1 parent 3c64ead commit 7510c5d
Show file tree
Hide file tree
Showing 27 changed files with 890 additions and 106 deletions.
8 changes: 3 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,17 @@ let package = Package(
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "SpeziFoundation", package: "SpeziFoundation"),
.product(name: "SpeziBluetooth", package: "SpeziBluetooth"),
.product(name: "BluetoothServices", package: "SpeziBluetooth"),
.product(name: "BluetoothViews", package: "SpeziBluetooth") // TODO: just because of the One protocol???
.product(name: "SpeziBluetoothServices", package: "SpeziBluetooth")
],
plugins: [swiftLintPlugin]
),
.target(
name: "SpeziDevicesUI",
dependencies: [
.target(name: "SpeziDevices"),
.product(name: "SpeziBluetooth", package: "SpeziBluetooth"),
.product(name: "BluetoothViews", package: "SpeziBluetooth"),
.product(name: "SpeziViews", package: "SpeziViews"),
.product(name: "SpeziValidation", package: "SpeziViews"),
.product(name: "SpeziBluetooth", package: "SpeziBluetooth"),
.product(name: "ACarousel", package: "ACarousel")
],
resources: [
Expand All @@ -70,7 +68,7 @@ let package = Package(
dependencies: [
.target(name: "SpeziDevices"),
.product(name: "SpeziBluetooth", package: "SpeziBluetooth"),
.product(name: "BluetoothServices", package: "SpeziBluetooth")
.product(name: "SpeziBluetoothServices", package: "SpeziBluetooth")
],
plugins: [swiftLintPlugin]
),
Expand Down
3 changes: 2 additions & 1 deletion Sources/SpeziDevices/DeviceManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
// SPDX-License-Identifier: MIT
//

import BluetoothServices
import OrderedCollections
import Spezi
import SpeziBluetooth
import SpeziBluetoothServices
import SwiftUI

// TODO: Start SpeziDevices generalization
Expand Down Expand Up @@ -46,6 +46,7 @@ public final class DeviceManager: Module, EnvironmentAccessible, DefaultInitiali
}

@Application(\.logger) @ObservationIgnored private var logger
@Dependency @ObservationIgnored private var tipKit: ConfigureTipKit

Check failure on line 49 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package iOS / Test using xcodebuild or run fastlane

cannot find type 'ConfigureTipKit' in scope

Check failure on line 49 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package iOS / Test using xcodebuild or run fastlane

cannot find type 'ConfigureTipKit' in scope

Check failure on line 49 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package visionOS / Test using xcodebuild or run fastlane

cannot find type 'ConfigureTipKit' in scope

Check failure on line 49 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package macOS / Test using xcodebuild or run fastlane

cannot find type 'ConfigureTipKit' in scope

Check failure on line 49 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package macOS / Test using xcodebuild or run fastlane

cannot find type 'ConfigureTipKit' in scope

Check failure on line 49 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / CodeQL / Test using xcodebuild or run fastlane

cannot find type 'ConfigureTipKit' in scope
@Dependency @ObservationIgnored private var bluetooth: Bluetooth?

required public init() {} // TODO: configure automatic search without devices paired!

Check warning on line 52 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package watchOS / Test using xcodebuild or run fastlane

Modifier Order Violation: public modifier should come before required (modifier_order)

Check warning on line 52 in Sources/SpeziDevices/DeviceManager.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package watchOS / Test using xcodebuild or run fastlane

Todo Violation: TODOs should be resolved (configure automatic search wit...) (todo)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SpeziDevices/Devices/BatteryPoweredDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
// SPDX-License-Identifier: MIT
//

import BluetoothServices
import SpeziBluetooth
import SpeziBluetoothServices


/// A battery powered Bluetooth device.
Expand Down
53 changes: 53 additions & 0 deletions Sources/SpeziDevices/Devices/GenericBluetoothPeripheral.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// This source file is part of the Stanford Spezi open-source project
//
// SPDX-FileCopyrightText: 2024 Stanford University and the project authors (see CONTRIBUTORS.md)
//
// SPDX-License-Identifier: MIT
//

import SpeziBluetooth


/// A generic bluetooth peripheral.
public protocol GenericBluetoothPeripheral {
/// The user-visible label.
///
/// This label is used to communicate information about this device to the user.
var label: String { get }

/// An optional accessibility label.
///
/// This label is used as the accessibility label within views when
/// communicate information about this device to the user.
var accessibilityLabel: String { get }

/// The current peripheral state.
var state: PeripheralState { get }

/// Mark the device to require user attention.
///
/// Marks the device to require user attention. The user should navigate to the details
/// view to get more information about the device.
var requiresUserAttention: Bool { get }
}


extension GenericBluetoothPeripheral {
/// Default implementation using the devices `label`.
public var accessibilityLabel: String {
label
}

/// By default the peripheral doesn't require user attention.
public var requiresUserAttention: Bool {
false
}
}


extension BluetoothPeripheral: GenericBluetoothPeripheral {
public nonisolated var label: String {
name ?? "Generic Peripheral"
}
}
6 changes: 1 addition & 5 deletions Sources/SpeziDevices/Devices/GenericDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,11 @@
// SPDX-License-Identifier: MIT
//

import BluetoothServices
import BluetoothViews
import Foundation
import SpeziBluetooth
import SpeziBluetoothServices


// TODO: move GenericBluetoothPeripheral to here?
// TODO: => merge BluetoothViews into SpeziDevicesUI?

/// A generic Bluetooth device.
///
/// A generic Bluetooth device that provides access to basic device information.
Expand Down
3 changes: 2 additions & 1 deletion Sources/SpeziDevices/Health/HealthDevice+HKDevice.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import HealthKit


extension HealthDevice {
public var hkDevice: HKDevice { // TODO: doesn't necessarily need to be public if we move MeasurementManager!
/// The HealthKit Device description.
public var hkDevice: HKDevice {
HKDevice(
name: name,
manufacturer: deviceInformation.manufacturerName,
Expand Down
2 changes: 1 addition & 1 deletion Sources/SpeziDevices/Model/ImageReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import SwiftUI


/// Reference an Image Resource.
public enum ImageReference { // TODO: SpeziViews candidate!
public enum ImageReference {
/// Provides the system name for an image.
case system(String)
/// Reference an image from the asset catalog of a bundle.
Expand Down
54 changes: 12 additions & 42 deletions Sources/SpeziDevices/Model/PairedDeviceInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,31 @@ import Foundation


/// Persistent information stored of a paired device.
public struct PairedDeviceInfo { // TODO: observablen => resolves UI update issue!
public struct PairedDeviceInfo {
// TODO: observablen => resolves UI update issue!

Check warning on line 14 in Sources/SpeziDevices/Model/PairedDeviceInfo.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package watchOS / Test using xcodebuild or run fastlane

Todo Violation: TODOs should be resolved (observablen => resolves UI upd...) (todo)
// TODO: update properties (model, lastSeen, battery) with Observation framework and not via explicit calls in the device class
// => make some things have internal setters(?)
// TODO: additionalData: lastSequenceNumber: UInt16?, userDatabaseNumber: UInt32?, consentCode: UIntX

/// The CoreBluetooth device identifier.
public let id: UUID
/// The device type.
///
/// Stores the associated ``PairableDevice/deviceTypeIdentifier-9wsed`` device type used to locate the device implementation.
public let deviceType: String // TODO: verify link
public let deviceType: String
/// Visual representation of the device.
public let icon: ImageReference?
/// A model string of the device.
public let model: String? // TODO: this one as well!
public let model: String?

// TODO: make some things have internal setters?
/// The user edit-able name of the device.
public var name: String
/// The date the device was last seen.
public var lastSeen: Date // TODO: don't set within the device class itself
public var lastSeen: Date
/// The last reported battery percentage of the device.
public var lastBatteryPercentage: UInt8? // TODO: update those values based on the Observation framework?

public var lastBatteryPercentage: UInt8?

public var lastSequenceNumber: UInt16?
public var userDatabaseNumber: UInt32? // TODO: default value?
// TODO: consent code?
// TODO: last transfer time?
// TODO: handle extensibility?
// TODO: how with codability? public var additionalData: [String: Any]

/// Create new paired device information.
/// - Parameters:
Expand All @@ -46,18 +45,14 @@ public struct PairedDeviceInfo { // TODO: observablen => resolves UI update issu
/// - icon: The device icon.
/// - lastSeen: The date the device was last seen.
/// - batteryPercentage: The last known battery percentage of the device.
/// - lastSequenceNumber: // TODO: docs
/// - userDatabaseNumber: // TODO: docs
public init(
id: UUID,
deviceType: String,
name: String,
model: String?,
icon: ImageReference?,
lastSeen: Date = .now,
batteryPercentage: UInt8? = nil,
lastSequenceNumber: UInt16? = nil,
userDatabaseNumber: UInt32? = nil
batteryPercentage: UInt8? = nil
) {
self.id = id
self.deviceType = deviceType
Expand All @@ -66,8 +61,6 @@ public struct PairedDeviceInfo { // TODO: observablen => resolves UI update issu
self.icon = icon
self.lastSeen = lastSeen
self.lastBatteryPercentage = batteryPercentage
self.lastSequenceNumber = lastSequenceNumber
self.userDatabaseNumber = userDatabaseNumber
}
}

Expand All @@ -84,29 +77,6 @@ extension PairedDeviceInfo: Hashable {

#if DEBUG
extension PairedDeviceInfo {
/*
// TODO: bring back those???
static var mockBP5250: PairedDeviceInfo {
PairedDeviceInfo(
id: UUID(),
deviceType: BloodPressureCuffDevice.deviceTypeIdentifier,
name: "BP5250",
model: OmronModel.bp5250,
icon: .asset("Omron-BP5250")
)
}

static var mockSC150: PairedDeviceInfo {
PairedDeviceInfo(
id: UUID(),
deviceType: WeightScaleDevice.deviceTypeIdentifier,
name: "SC-150",
model: OmronModel.sc150,
icon: .asset("Omron-SC-150")
)
}
*/

/// Mock Health Device 1 Data.
@_spi(TestingSupport) public static var mockHealthDevice1: PairedDeviceInfo {
PairedDeviceInfo(
Expand Down
3 changes: 2 additions & 1 deletion Sources/SpeziDevices/Model/PairingContinuation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public final class PairingContinuation {
private var isInSession = false
private var pairingContinuation: CheckedContinuation<Void, Error>?

/// Create a new pairing continuation management object.
public init() {}

func pairingSession<T>(_ action: () async throws -> T) async throws -> T {
Expand All @@ -30,7 +31,7 @@ public final class PairingContinuation {
}

defer {
lock.withLock{
lock.withLock {
isInSession = false
}
}
Expand Down
1 change: 1 addition & 0 deletions Sources/SpeziDevices/SpeziDevices.docc/SpeziDevices.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ SPDX-License-Identifier: MIT

### Devices

- ``GenericBluetoothPeripheral``
- ``GenericDevice``
- ``BatteryPoweredDevice``
- ``PairableDevice``
Expand Down
1 change: 1 addition & 0 deletions Sources/SpeziDevicesUI/Devices/DeviceDetailsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public struct DeviceDetailsView: View {
.navigationBarTitleDisplayMode(.inline)
.confirmationDialog("Do you really want to forget this device?", isPresented: $presentForgetConfirmation, titleVisibility: .visible) {
Button("Forget Device", role: .destructive) {
// TODO: message to check for ConfigureTipKit dependency!
ForgetDeviceTip.hasRemovedPairedDevice = true
deviceManager.forgetDevice(id: deviceInfo.id)
dismiss()
Expand Down
1 change: 1 addition & 0 deletions Sources/SpeziDevicesUI/Devices/DevicesGrid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public struct DevicesGrid: View {
if devices.isEmpty {
ZStack {
VStack {
// TODO: message to check for ConfigureTipKit dependency!
TipView(ForgetDeviceTip.instance)
.padding([.leading, .trailing], 20)
Spacer()
Expand Down
Loading

0 comments on commit 7510c5d

Please sign in to comment.