Releases: JuulLabs/kable
0.35.0-rc
Tip
The SensorTag sample app has been updated to use this release of Kable.
🚀 Changes
This release includes major internal re-workings of Kable, as well as some externally facing API changes. Most notably, the scanning and connection handling internals have been refactored to better leverage structured concurrency and provide a more predictable connection shutdown procedure.
The exception hierarchy has been significantly simplified, making error handling more consistent and possible in common code.
In general, you should now only ever need to catch IOException
when calling Kable functions, all other exceptions should be considered programming error (could've been prevented by pre-checking conditions). Pre-conditions that could change between a check and call are thrown as IOException
s.
Deprecate Peripheral
creation via CoroutineScope
extension function
Creating a Peripheral
via CoroutineScope
extension function has been deprecated.
A Peripheral
's connection needs to be explicitly managed, having its lifecycle be implicitly governed by a parent CroutineScope
provided a footgun.
It is clearer / easier to reason about if a Peripheral
shall be disposed of manually when it is no longer needed: the Peripheral.cancel
function provides an explicit means of disposing of a Peripheral
.
Old | New |
---|---|
val scope: CoroutineScope
val peripheral = scope.peripheral(advertisement) {
// Configure peripheral.
} |
val peripheral = Peripheral(advertisement) {
// Configure peripheral.
} |
val peripheral: Peripheral = ..
peripheral.disconnect()
scope.cancel() // Dispose of `Peripheral`. |
val peripheral: Peripheral = ..
peripheral.disconnect()
peripheral.cancel() // Dispose of `Peripheral`. |
Peripheral
is now a CoroutineScope
Every Peripheral
is now itself a CoroutineScope
. You can launch
jobs from peripherals that you wish to run until the Peripheral
is disposed (via Peripheral.cancel
).
val peripheral: Peripheral = ..
peripheral.launch {
// Long running task that will be shutdown when `Peripheral` is disposed via `cancel`.
}
Every connection is now a CoroutineScope
Whenever a connection is established (Peripheral.connect
) the function call returns a CoroutineScope
that can be used to launch
jobs that should run until the connection is terminated (either via Peripheral.disconnect
or connection drop).
val peripheral: Peripheral = ..
val scope = peripheral.connect()
scope.launch {
// Long running task that will be shutdown when connection terminates.
}
Deprecate Bluetooth.availability
Having a consistent means of providing realtime bluetooth availability (Bluetooth.availability
flow) proved impossible — mostly due to Core Bluetooth limitations. As such, it is expected that library consumers will roll their own mechanism of determining bluetooth availability, and Bluetooth.availability
will be removed in a future version. See #737 for more details.
Dropped kable-exceptions
Maven artifact
The kable-exceptions
Maven artifact has been dropped in favor of having exceptions provided by kable-core
artifact. If you were previously pulling in com.juul.kable:kable-exceptions
dependency, you should remove it and you only need to pull in com.juul.kable:kable-core
.
Release candidate
This release is considered a "release candidate" as it has not had thorough testing to be considered ready for wider use. The API surface changed significantly as well, and may warrant additional feedback to drive its final form. Please try this version and report any issues.
Common
- Drop
kable-exceptions
module and remove unused exceptions (#769) - Deprecate
Bluetooth.availability
(#772) - Fix advertisement equality/hash (#759)
- Leverage structured concurrency for connection handling (#749)
- Prevent bluetooth permission dialog when spinning up
CBCentralManager
(#739) - Add
Bluetooth.isSupported()
function (#738) - Unify scan exceptions (#733)
- Thanks to @FilippoVigani for reporting #103
- Thanks to @mark-dumontier for reporting #239, and @BluestormDNA for adding details
- Escalate deprecations (#732)
- Use
IOException
provided by kotlinx-io (#728)
Android
- Drop
ConnectionRejectedException
(#771) - Handle when peripheral services change (#754), thanks to @JonatanPlesko for reporting!
- Add basic threading strategy support (#612)
Apple
- Have
allowDuplicateKeys
default totrue
(#760) - Make
isSupported
alwaystrue
on Apple (#752) - Handle when peripheral services change (#754)
JavaScript
- Use
jso
from kotlin-wrappers library (#726)
🧰 Maintenance
- Fix broken links in README.md (#775), special thanks to @maribox!
- Fix closing parenthesis in
Options
builder example in README (#727), special thanks to @khebrati! - Suppress Kotlin compilation warnings on CI (#734)
- Update dependency org.jetbrains.kotlinx:kotlinx-io-core to v0.5.4 (#745, #757)
- Update plugin android-library to v8.7.1 (#741, #763, #770, #777)
- Update plugin api to v0.16.3 (#747)
- Update coroutines to v1.9.0 (#767)
- Update dependency gradle to v8.10.2 (#751)
- Update dependency org.jetbrains.kotlin-wrappers:kotlin-wrappers-bom to v1.0.0-pre.819 (#735, #740, #742, #744, #746, #750, #756, #762, #766, #773, #776)
- Update dependency org.jetbrains.kotlin.multiplatform to v2.0.21 (#736, #743, #774)
0.34.0
Note
This version of Kable provides a DSL for specifying scan filters and should be used instead of the old deprecated filter list (which will be removed in a future Kable release).
Some example migrations to the new scan filter DSL:
Deprecated | New filter DSL |
---|---|
Scanner {
filters = listOf(Filter.Name("My device"))
} |
Scanner {
filters {
match {
name = Filter.Name.Exact("My device")
}
}
} |
Scanner {
filters = listOf(
Filter.Service(uuidFrom(..)),
Filter.NamePrefix("Ex"),
)
} |
Scanner {
filters {
match {
services = listOf(uuidFrom(..))
}
match {
name = Filter.Name.Prefix("Ex")
}
} |
val options = Options(
filters = listOf(
Filter.NamePrefix("Example"),
),
optionalServices = listOf(
uuidFrom(..),
),
)
scope.requestPeripheral(options).await() |
val options = Options {
filters {
match {
name = Filter.Name.Prefix("Example")
}
}
optionalServices = listOf(
uuidFrom(..),
)
)
scope.requestPeripheral(options).await() |
See Scanning section of README and #695 for more details.
Android
- Fix nullability of
connectGattCompat
function (#720)
Apple
- Fix reading of descriptor value as
NSData
(#706), special thanks to @FabioCornelli for the contribution
🧰 Maintenance
0.33.2
Apple
- Discover descriptors on connect (#704), special thanks to @FabioCornelli for the contribution
🧰 Maintenance
- Use GitHub Actions for documentation deployment (#702)
0.33.1
0.33.0
Important
Kable's Maven artifact IDs have changed (prefixed with kable-
):
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.juul.kable:kable-core:${kableVersion}")
implementation("com.juul.kable:kable-exceptions:${kableVersion}")
implementation("com.juul.kable:kable-log-engine-khronicle:${kableVersion}")
}
}
}
Kable's package names were not changed, only the Maven coordinates; you should only need to update your Gradle configuration (e.g. libs.versions.toml
).
Kotlin 2.0.0 (K2) Upgrade
Kable is now built against Kotlin 2.0.0 (K2). 🎉
Note
With this upgrade, the Kotlin compiler became more restrictive with expect
/actual
declarations, as a result, some Kable APIs have changed:
- Advertisement interfaces (e.g.
AndroidAdvertisement
,CoreBluetoothAdvertisement
, etc) have been consolidated to a commonPlatformAdvertisement
Scanner
interface now defines a generic type for advertisements it will emit:Scanner<PlatformAdvertisement>
For most users, these changes should not impact your code. Some notable instances where you'll need to make code changes:
If you hold a reference to a Scanner
in a variable or property, you'll need to change Scanner
to use the PlatformScanner
type alias, or specify the generic type explicitly, for example:
val scanner: PlatformScanner // or...
val scanner: Scanner<PlatformAdvertisement>
If you have any classes that implement the Scanner
interface, you'll need to change the interface to PlatformScanner
(or Scanner<PlatformAdvertisement>
) and change the overridden type of the advertisements
property to PlatformAdvertisement
, for example:
class ExampleScanner(..) : PlatformScanner {
override val advertisements: Flow<PlatformAdvertisement> = ..
}
🚀 Changes
- Update dependency org.jetbrains.kotlin.multiplatform to v2 (#683)
- Replace Tuulbox logging w/ Khronicle (#690)
- Prefix Maven artifacts with
kable-
(#692) - Remove
log-engine-tuulbox
module (#689)
🧰 Maintenance
0.32.0
Android
- Provide ability to pre-conflate scanned advertisements (#684), special thanks to @djweber for the contribution
🧰 Maintenance
- Update plugin android-library to v8.4.1 (#674, #682)
- Update coroutines to v1.8.1 (#678)
- Migrate to
gradle/actions/setup-gradle
(#680) - Update dependency com.juul.khronicle:khronicle-core to v0.3.0 (#676)
- Update dependency org.jetbrains.kotlin.multiplatform to v1.9.24 (#675)
- Update dependency androidx.core:core-ktx to v1.13.1 (#671, #673)
- Update gradle/wrapper-validation-action action to v3 (#672)
- Update atomicfu to v0.24.0 (#670)
0.31.1
0.31.0
0.30.0
- Log engine for Khronicle logging (#644)
Android
- Provide write failure as property of exception (#648), thanks to @ArtemBurmistrov for reporting the issue
- Make
AndroidAdvertisement
parcelable (#652), thanks to @eddieSullivan for reporting the issue
🧰 Maintenance
- Explicitly pull in AtomicFU dependency (#656)
- Disable
AndroidGradlePluginVersion
lint check (#657) - Update plugin maven-publish to v0.28.0 (#655)
- Simplify
binary-compatibility-validator
setup (#651) - Replace deprecated Gradle
buildDir
(#650) - Update AtomicFU configuration (#649)
- Remove explicit
POM_ARTIFACT_ID
configuration (#645) - Update dependency org.jetbrains.kotlin.multiplatform to v1.9.23 (#646)
- Update plugin dokka to v1.9.20 (#647)
- Update plugin android-library to v8.3.0 (#640)
- Update binary-compatibility-validator to v0.14.0 (#639)
- Remove workaround for
jvmToolchain
not being honored pre AGP 8.1.0-alpha09 (#636) - Update plugin kotlinter to v4.2.0 (#623)
0.29.1
Android
- Defensive copying in Android legacy, deprecated
BluetoothGattCallback
functions (#635)