Skip to content

Commit

Permalink
Swift 6 compatibility (#41)
Browse files Browse the repository at this point in the history
# Swift 6 compatibility

## ♻️ Current situation & Problem
This PR adds support for Strict Concurrency Checking in SpeziBluetooth.
With Xcode 16 beta 3, there are still some warnings about non-sendable
key paths (in the UITest app), however this will be resolved with the
next Xcode 16 beta (this is already resolved in the current release/6.0
swift toolchain snapshot).

## ⚙️ Release Notes 
* SpeziBluetooth now uses the `@SepziBluetooth` global actor to isolate
all its state. All current `actor` types were migrated to `classes` that
are isolated to the global actor.
* All uses of CoreBluetooth's CBUUID are replaced with a new string
literal expressible `BTUUID` type that is Sendable.
All places that declared a String overload for `CBUUID`-based arguments
(e.g., the `Characteristic` initializer or `DiscoveryCriteria` methods)
removed these overloads in favor of the expressible by string literal
conformance of `BTUUID`.
* We removed all accesses to characteristic descriptors from the
`@Characteristic` property wrapper. Descriptors are currently not
supported anyways, something that is tracked in #11.
* We removed the `BluetoothPeripheral/localName` property (also
`@DeviceState(\.localName)`). Migrate your code to directly access the
`AdvertisementData/localName` property.
* We removed the `AdvertisementData/rawAdvertisementData` property and
respective initializers to provide Sendable guarantees. Please use new
typed initializers to initialize your testing data.
* The `BluetoothPeripheral/services` property is no longer accessible
via `@DeviceState` and is now isolated to the `@SpeziBluetooth` global
actor. Use the existing `@Service` property wrapper for any service
interactions.
* The new
`bluetoothScanningOptions(minimumRSSI:advertisementStaleInterval:)` view
modifier replaces direct assignment of environment values of
`minimumRSSI` and `advertisementStaleInterval`. Please migrate to using
the new modifier.
* Retrieving nearby devices using the Bluetooth module via
`Bluetooth/nearbyDevices(for:)` is now isolated to the `@MainActor` for
easy interaction with SwiftUI.
* It is now recommended to implement `BluetoothService`s as structs
(SpeziBluetoothServices was migrated accordingly).
* A lot of state was synchronized through lock-free primitives using the
Swift Atomics package 🚀


## :books: Documentation
All documentation was updated to reflect changes described above.


## :white_check_mark: Testing
Existing unit tests and UI tests are still passing 🚀 


## :pencil: Code of Conduct & Contributing Guidelines 

By submitting creating this pull request, you agree to follow our [Code
of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md):
- [x] I agree to follow the [Code of
Conduct](https://github.com/StanfordSpezi/.github/blob/main/CODE_OF_CONDUCT.md)
and [Contributing
Guidelines](https://github.com/StanfordSpezi/.github/blob/main/CONTRIBUTING.md).
  • Loading branch information
Supereg authored Jul 18, 2024
1 parent fe7cbfd commit 387ed5d
Show file tree
Hide file tree
Showing 94 changed files with 2,983 additions and 1,927 deletions.
24 changes: 23 additions & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ jobs:
runsonlabels: '["macOS", "self-hosted", "spezi"]'
scheme: SpeziBluetooth-Package
artifactname: SpeziBluetooth-Package.xcresult
resultBundle: SpeziBluetooth-Package.xcresult
packageios_latest:
name: Build and Test Swift Package iOS Latest
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macOS", "self-hosted", "spezi"]'
scheme: SpeziBluetooth-Package
xcodeversion: latest
swiftVersion: 6
artifactname: SpeziBluetooth-Package-Latest.xcresult
resultBundle: SpeziBluetooth-Package-Latest.xcresult
ios:
name: Build and Test iOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
Expand All @@ -32,6 +43,17 @@ jobs:
scheme: TestApp
artifactname: TestApp-iOS.xcresult
resultBundle: TestApp-iOS.xcresult
ios_latest:
name: Build and Test iOS Latest
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
with:
runsonlabels: '["macOS", "self-hosted", "spezi"]'
path: 'Tests/UITests'
scheme: TestApp
xcodeversion: latest
swiftVersion: 6
artifactname: TestApp-iOS-Latest.xcresult
resultBundle: TestApp-iOS-Latest.xcresult
macos:
name: Build and Test macOS
uses: StanfordSpezi/.github/.github/workflows/xcodebuild-or-fastlane.yml@v2
Expand All @@ -43,7 +65,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' -skipPackagePluginValidation -skipMacroValidation | 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 | xcbeautify"
secrets: inherit
uploadcoveragereport:
name: Upload Coverage Report
Expand Down
23 changes: 18 additions & 5 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import PackageDescription


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


Expand All @@ -37,6 +37,7 @@ let package = Package(
.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-atomics.git", from: "1.2.0"),
.package(url: "https://github.com/StanfordBDHG/XCTestExtensions.git", from: "0.4.11")
] + swiftLintPackage(),
targets: [
Expand All @@ -47,7 +48,8 @@ let package = Package(
.product(name: "NIO", package: "swift-nio"),
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "SpeziFoundation", package: "SpeziFoundation"),
.product(name: "ByteCoding", package: "SpeziNetworking")
.product(name: "ByteCoding", package: "SpeziNetworking"),
.product(name: "Atomics", package: "swift-atomics")
],
resources: [
.process("Resources")
Expand Down Expand Up @@ -82,10 +84,21 @@ let package = Package(
plugins: [] + swiftLintPlugin()
),
.testTarget(
name: "BluetoothServicesTests",
name: "SpeziBluetoothTests",
dependencies: [
.target(name: "SpeziBluetoothServices"),
.target(name: "SpeziBluetooth"),
.target(name: "SpeziBluetoothServices")
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
),
.testTarget(
name: "SpeziBluetoothServicesTests",
dependencies: [
.target(name: "SpeziBluetooth"),
.target(name: "SpeziBluetoothServices"),
.product(name: "XCTByteCoding", package: "SpeziNetworking"),
.product(name: "NIO", package: "swift-nio"),
.product(name: "XCTestExtensions", package: "XCTestExtensions")
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ Note that the value types needs to be optional and conform to
[`ByteCodable`](https://swiftpackageindex.com/stanfordspezi/spezifileformats/documentation/bytecoding/bytecodable) respectively.

```swift
class DeviceInformationService: BluetoothService {
struct DeviceInformationService: BluetoothService {
static let id: BTUUID = "180A"

@Characteristic(id: "2A29")
var manufacturer: String?
@Characteristic(id: "2A26")
Expand Down Expand Up @@ -249,8 +251,12 @@ class MyDevice: BluetoothDevice {
// declare dependency to a configured Spezi Module
@Dependency var measurements: Measurements

required init() {
weightScale.$weightMeasurement.onChange(perform: handleNewMeasurement)
required init() {}

func configure() {
weightScale.$weightMeasurement.onChange { [weak self] value in
self?.handleNewMeasurement(value)
}
}

private func handleNewMeasurement(_ measurement: WeightMeasurement) {
Expand Down
Loading

0 comments on commit 387ed5d

Please sign in to comment.