Skip to content

Releases: JuulLabs/kable

0.35.0-rc

23 Oct 05:39
823e0db
Compare
Choose a tag to compare
0.35.0-rc Pre-release
Pre-release

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 IOExceptions.

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)
  • Escalate deprecations (#732)
  • Use IOException provided by kotlinx-io (#728)

Logo Android

  • Drop ConnectionRejectedException (#771)
  • Handle when peripheral services change (#754), thanks to @JonatanPlesko for reporting!
  • Add basic threading strategy support (#612)

Logo Apple

  • Have allowDuplicateKeys default to true (#760)
  • Make isSupported always true on Apple (#752)
  • Handle when peripheral services change (#754)

Logo 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

29 Jul 19:56
c6d400d
Compare
Choose a tag to compare

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.

Logo Android

  • Fix nullability of connectGattCompat function (#720)

Logo Apple

  • Fix reading of descriptor value as NSData (#706), special thanks to @FabioCornelli for the contribution

🧰 Maintenance

  • Update plugin api to v0.16.2 (#717, #725)
  • Rename module directories to match Maven artifact names (#709)
  • Update plugin kotlinter to v4.4.1 (#705, #716)
  • Update plugin android-library to v8.5.1 (#712)
  • Update dependency gradle to v8.9 (#713)
  • Update atomicfu to v0.25.0 (#707)

0.33.2

05 Jul 23:39
c91f0fa
Compare
Choose a tag to compare

Logo Apple

  • Discover descriptors on connect (#704), special thanks to @FabioCornelli for the contribution

🧰 Maintenance

  • Use GitHub Actions for documentation deployment (#702)

0.33.1

27 Jun 07:49
ee0182c
Compare
Choose a tag to compare
  • Ensure teardown is completed when observation coroutines are canceled (#699), special thanks to @mmeisel for the contribution

Logo Apple

  • Cast to NSNumber for determining if advertisement is connectable (#700), special thanks to @R4leven for the bug report

🧰 Maintenance

  • Update plugin maven-publish to v0.29.0 (#701)
  • Update Gradle documentation w/ new Maven coordinate (#694)

0.33.0

18 Jun 20:27
f19edf0
Compare
Choose a tag to compare

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 common PlatformAdvertisement
  • 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

  • Update plugin android-library to v8.5.0 (#693)
  • Update tuulbox to v8 (major) (#687)
  • Update dependency gradle to v8.8 (#691)
  • Update tuulbox to v7.3.0 (#686)
  • Update Gradle documentation w/ new Maven coordinate (#694)

0.32.0

03 Jun 16:52
ad45bd3
Compare
Choose a tag to compare

Logo 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

18 Apr 07:59
7a58126
Compare
Choose a tag to compare

Logo Apple

  • Watch for connection loss during connect (#668), thanks to @cksgud for reporting and @muellnes for testing/validating the fix

🧰 Maintenance

  • Update plugin android-library to v8.3.2 (#667)
  • Update dependency com.juul.khronicle:khronicle-core to v0.2.0 (#666)

0.31.0

04 Apr 23:56
b500995
Compare
Choose a tag to compare
  • Fix crash in peripheral <init> when parent job is already complete (#665)
  • Use Tuulbox's broadcastReceiverFlow for bluetooth state (#658)

Logo JavaScript

  • Re-align the filter shape to work correctly with the browser API (#661)

🧰 Maintenance

  • Update plugin kotlinter to v4.3.0 (#664)
  • Update dependency gradle to v8.7 (#663)
  • Update plugin android-library to v8.3.1 (#660)
  • Update dependency com.benasher44:uuid to v0.8.4 (#659)

0.30.0

21 Mar 18:29
ff220fc
Compare
Choose a tag to compare
  • Log engine for Khronicle logging (#644)

Logo 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

26 Feb 18:31
75b058b
Compare
Choose a tag to compare

Logo Android

  • Defensive copying in Android legacy, deprecated BluetoothGattCallback functions (#635)

🧰 Maintenance

  • Update tuulbox to v7.2.0 (#628)
  • Update coroutines to v1.8.0 (#633)
  • Update release-drafter/release-drafter action to v6 (#631)
  • Update dependency gradle to v8.6 (#630)
  • Update gradle/wrapper-validation-action action to v2 (#629)