-
Notifications
You must be signed in to change notification settings - Fork 0
Add functional requirements for service & characteristic discovery #47
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,7 +26,7 @@ ReliaBLE is a Swift package that provides a reliable, modern, yet easy to use in | |
| 1. Reliability of Communication: | ||
|
|
||
| - FR-1.1: Implement error detection and correction mechanisms for each BLE transaction. | ||
| - FR-1.2: Ensure automatic reconnection attempts with exponential backoff for failed connections. | ||
| - FR-1.2: Ensure automatic reconnection attempts with exponential backoff for failed connections. On reconnection, services and characteristics must be re-discovered rather than reused from the prior connection (see FR-10.6). | ||
| - FR-1.3: Provide status updates on connection stability and data transmission integrity. | ||
| - FR-1.3.1: Provide status updates on connection stability (e.g. connected, disconnected, reconnecting). | ||
| - FR-1.3.2: Provide status updates on data transmission integrity. | ||
|
|
@@ -49,7 +49,7 @@ ReliaBLE is a Swift package that provides a reliable, modern, yet easy to use in | |
|
|
||
| 4. Command Style Protocol for Interacting with Peripherals: | ||
|
|
||
| - FR-4.1: Define a flexible command protocol where the content and functionality of commands are provided by the integrating app. | ||
| - FR-4.1: Define a flexible command protocol where the content and functionality of commands are provided by the integrating app. Commands target services and characteristics made available through discovery (see FR-10), referenced by UUID (CBUUID). | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good cross-ref — make the gate explicit. "Commands target services/characteristics made available through discovery" still allows interpreting discovery as a passive catalog. Please strengthen to something like:
This is the minimal FR-4 tie-in needed so the later command-queue work inherits the gate instead of re-litigating it. |
||
|
|
||
| - FR-4.2: Support various command types: | ||
| - FR-4.2.1: Notify-only (peripherals notify with updates). | ||
|
|
@@ -122,10 +122,54 @@ ReliaBLE is a Swift package that provides a reliable, modern, yet easy to use in | |
| - Connection/disconnection events | ||
| - Command successes or failures | ||
| - ✅ Scanning start/stop | ||
| - Service/characteristic discovery events (discovery start/completion/failure, GATT table changes, subscription state changes) | ||
| - Security events (e.g., encryption initiation or failure) | ||
| - Data chunking operations | ||
|
|
||
|
|
||
| 10. Service and Characteristic Discovery and Management: | ||
|
|
||
| The layer between establishing a connection (FR-3, FR-5) and reading or writing data | ||
| (FR-4, FR-7). After a peripheral connects, its GATT services and characteristics must be | ||
| discovered before any interaction, and that discovered view must be kept correct across | ||
| firmware changes and reconnections. This section covers discovery and characteristic setup | ||
| only; the movement of application data over characteristics is covered by the command | ||
| protocol (FR-4) and chunking (FR-7). | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Readiness as reliability invariant (missing). This intro correctly positions discovery between connect and data movement, but it still describes discovery as a catalog/setup phase rather than a gate. Please add an explicit invariant here (or as FR-10.3.x), roughly:
This is the ObjC "discovery gates the command queue" property that the architecture comparison flagged as the first reliability primitive to land with GATT. Without it, FR-4/5 implementors will race writes against incomplete discovery. |
||
|
|
||
| - FR-10.1: Service Discovery: | ||
| - FR-10.1.1: Provide an API to discover services on a connected peripheral, requiring the integrating app to declare the specific service UUIDs of interest. Do not expose unfiltered "discover all services" as the default path — Apple explicitly discourages it because enumerating an entire remote GATT table negatively affects battery life and time-to-ready. | ||
| - FR-10.1.2: Support both automatic discovery on connection (using a caller-declared set of desired services/characteristics) and explicit, on-demand discovery invoked by the integrating app. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. API shape for "caller-declared" services is underspecified. FR-10.1.1–10.1.2 require a declared UUID set for auto-on-connect discovery, but not where that declaration lives. ReliaBLE's connection model is explicit
Also specify: if auto-discovery is enabled and no UUID set was declared, is that a hard error (preferred — matches "no unfiltered default") or a no-op leave-not-ready? Please nail this at requirement level so implementation doesn't invent three competing APIs. |
||
| - FR-10.1.3: Expose discovered services to the integrating app as Sendable value snapshots keyed by peripheral identifier and service UUID (CBUUID), mirroring the existing peripheral-snapshot pattern. Never surface live CoreBluetooth objects. | ||
|
|
||
| - FR-10.2: Characteristic and Descriptor Discovery: | ||
| - FR-10.2.1: Provide an API to discover characteristics for a discovered service, allowing the integrating app to declare the characteristic UUIDs of interest. Enforce CoreBluetooth's ordering constraint: services are discovered before characteristics, and characteristics before descriptors. | ||
| - FR-10.2.2: Track per-service discovery completion, correctly handling the separate completion callback fired for each service, and report a peripheral as "ready" only once all requested discoveries have completed. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Ready" needs a hard consumer contract, not just a status bit. "Report a peripheral as ready only once all requested discoveries have completed" is good, but incomplete without:
Without (2)–(3), "ready" is informational only and won't prevent the early-I/O races this section exists to stop. |
||
| - FR-10.2.3: Optionally discover descriptors for a characteristic on demand. Descriptor discovery is not required to enable notifications (see FR-10.4.1). | ||
| - FR-10.2.4: Expose discovered characteristics as Sendable value snapshots including metadata: UUID (CBUUID), properties (read, write, write-without-response, notify, indicate), and, when discovered, descriptors. | ||
|
|
||
| - FR-10.3: Discovery Lifecycle and Readiness: | ||
| - FR-10.3.1: Model discovery as an explicit, per-peripheral state machine driven entirely by delegate callbacks. Never assume synchronous availability of services or characteristics after initiating discovery. | ||
| - FR-10.3.2: Provide callbacks/streams (mirroring the asynchronous event pattern of FR-2.3) reporting discovery progress, completion, and errors. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Discovery lifecycle vs existing ReliaBLE already ships connection lifecycle streams ( Please specify one of:
Architecture eval preference: keep connection recovery observability (a ReliaBLE strength) distinct, and treat readiness as an explicit gate/stream so reconnect ladders don't get conflated with GATT catalog state. Either way, pick it in the PRD so implementers don't fork models. |
||
| - FR-10.3.3: Enforce a configurable discovery timeout so that a stalled or interrupted discovery surfaces as an error rather than an indefinite wait. | ||
| - FR-10.3.4: Handle interrupted or partial discovery: if a peripheral disconnects mid-discovery, treat it as a hard reset of the discovery state — discard partial results and any pending discovery bookkeeping — and re-run discovery on the next connection (see FR-10.6.1). | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good hard-reset on mid-discovery disconnect — keep this. One addition: on unexpected disconnect, also define what happens to any app-visible discovery snapshot (clear immediately vs mark stale until next ready). FR-10.6.2 says discard internal CB refs; the app-facing catalog invalidation timing should be equally explicit to match the existing peripheral-snapshot refresh philosophy. |
||
| - FR-10.3.5: Handle non-nil errors returned in each discovery callback, mapping CoreBluetooth errors into the library's error type. Distinguish common failure causes where feasible (e.g., characteristic-not-found from an overly narrow filter or a stale cache, versus disconnection during discovery). | ||
|
|
||
| - FR-10.4: Characteristic Subscription Management (Notify/Indicate): | ||
| - FR-10.4.1: Provide an API to enable and disable notifications or indications on a characteristic using the platform's subscribe mechanism. Never write the Client Characteristic Configuration Descriptor (CCCD, 0x2902) directly — CoreBluetooth manages the CCCD write internally and forbids writing it directly. | ||
| - FR-10.4.2: Confirm the subscription state-change callback before treating a subscription as active, and sequence subscription-enable before issuing any command that causes the peripheral to emit data, so that initial notifications are not lost. Delivery of the notified values is covered by FR-4.2.1 (notify-only) and FR-2.3.2 (data-received streams). | ||
| - FR-10.4.3: Expose subscription state and its changes to the integrating app. Do not rely solely on the platform's `isNotifying` flag, which can lag the actual state. | ||
| - FR-10.4.4: Account for the fact that notifications are unacknowledged at the GATT layer and may be lost or reordered above the link layer. Application-layer framing/sequencing for multi-packet notification streams is handled per FR-7 (chunking and reassembly). | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FR-10.4 scope + reconnect re-subscribe gap. Two issues:
Also sequence re-subscribe before releasing the readiness gate for commands that depend on notifications (ties to FR-10.4.2). |
||
|
|
||
| - FR-10.5: GATT Cache Invalidation and Re-discovery: | ||
| - FR-10.5.1: Detect and surface peripheral GATT-table changes reported by the platform (the "did modify services" callback) and automatically re-run discovery for the affected services. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ObjC re-suspended the peripheral queue on GATT table change, then rediscovered, then resumed. FR-10.5.1 only requires detecting the callback and re-running discovery. Please require:
Otherwise commands can execute against a half-invalidated catalog mid-DFU/service-change — exactly the class of bug the readiness gate exists to prevent. |
||
| - FR-10.5.2: Treat reconnection following a peripheral firmware update or DFU as requiring full re-discovery; do not trust a previously cached view of services and characteristics. | ||
| - FR-10.5.3: Document the iOS OS-level GATT caching limitation: there is no public API to clear the cache. Reliable cross-connection refresh requires the peripheral to implement the Service Changed characteristic (0x2A05) within the Generic Attribute Service (0x1801) and to bond; otherwise the only fallback is user-driven (toggle Bluetooth, or "Forget This Device"). This is a peripheral-firmware responsibility that the library cannot fully control, and the limitation must be communicated to integrating apps. | ||
|
|
||
| - FR-10.6: Reconnection and Stale-Reference Handling: | ||
| - FR-10.6.1: Re-discover services and characteristics on every new connection. Never reuse a service or characteristic reference obtained from a prior connection (see FR-1.2 for reconnection). | ||
| - FR-10.6.2: Internally discard all live CoreBluetooth service/characteristic references on disconnect and reset the discovery state. The app-facing catalog must be keyed by UUID (CBUUID), not by held CoreBluetooth objects. This is consistent with the library's existing invalidation/refresh behavior on Bluetooth power cycling. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. State restoration path is missing. FR-10.6 covers "every new connection" and disconnect invalidation, but ReliaBLE already has CB state restoration ( Please add something like FR-10.6.3:
Also: "keyed by peripheral identifier" should explicitly mean ReliaBLE's |
||
|
|
||
|
|
||
| ### Non-Functional Requirements | ||
|
|
||
| 1. Modern Swift 6 Architecture: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-discover on reconnect is correct; incomplete for in-flight work.
Within GATT scope this FR-1.2 addition is right. Please also note (one clause is enough) that re-discovery is part of returning to ready, and that any future command-layer reconnect-and-rerun (FR-4/5; post-merge) depends on this ready transition — so we don't later treat "connected again" as sufficient to resume I/O.
No need to specify the full pause/retry model here.