Skip to content

Improve bounded iBeacon ranging and region reconciliation - 3 - #5735

Draft
Pixelobserver wants to merge 7 commits into
home-assistant:mainfrom
Pixelobserver:prep/split-5629-beacon-ranging-final-v2
Draft

Improve bounded iBeacon ranging and region reconciliation - 3#5735
Pixelobserver wants to merge 7 commits into
home-assistant:mainfrom
Pixelobserver:prep/split-5629-beacon-ranging-final-v2

Conversation

@Pixelobserver

Copy link
Copy Markdown

AI Policy

Select exactly one option that describes AI usage in this contribution:

  • I have not used AI for this contribution.
  • AI assistance was used for this contribution.
  • AI fully generated the code for this contribution, but I've reviewed and understood it before submitting and will respond without AI during review.

Summary

Improve iBeacon transition verification with bounded ranging. This is part 3 of the split requested for #5629.

  • Verify entry using usable ranging samples and reconcile exits using repeated empty samples plus an elapsed-time threshold.
  • Share ranging constraints across active consumers and acquire background execution before starting bounded background scans.
  • Bound retries and cancel delayed work when it is no longer needed.
  • Read current stored zone state before emitting entry events to preserve reentry and avoid duplicates or events for deleted zones.
  • Respect Core Location's combined monitored-region limit.
  • Add lifecycle, cancellation, state-freshness and region-limit regression tests, using controlled time in collector tests.

This collector/filter foundation is independent of #5703 and #5730. Existing Core Location delegate paths use the ranging logic; the remaining lifecycle integration and durable event delivery follow separately in part 4.

Screenshots

N/A — no visual changes or new settings.

Link to pull request in Documentation repository

No documentation PR yet. This change improves verification of existing beacon behavior without adding configuration options.

Any other notes

Completed local validation:

  • SwiftFormat 0.53.1 on the prepared code.
  • Feature scope and whitespace checks.
  • Current main integrated without conflicts.
  • Xcode test target and Tests-Unit scheme membership inspected.

Pending CI verification:

  • Apple-platform compilation and actual XCTest execution.
  • Full lint checks, including SwiftLint.
  • Measured patch coverage meeting the unchanged 90% threshold.

Twelve additional regression test cases were added during preparation. This describes available tests, not a completed test run. Real-device ranging and battery behavior have not been verified for this revision.

Copilot AI lite review requested due to automatic review settings September 11, 2026 05:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Five unresolved moderate findings affect background-task coverage and collector lifecycle, state, and reconciliation behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Improves iBeacon verification and region reconciliation with bounded ranging, retry handling, background execution, and region-limit enforcement.

Changes:

  • Adds ranging-based entry verification and exit reconciliation.
  • Shares scan constraints and bounds retries, cancellation, and execution time.
  • Adds lifecycle, freshness, cancellation, and region-limit regression coverage.

Five moderate findings remain regarding background-task protection and collector scan, state, and reconciliation handling.

File summaries
File Description
Tests/Shared/AppZone.test.swift Updates region assertion formatting.
Tests/App/ZoneManager/ZoneManagerRegionFilter.test.swift Tests combined region limits.
Tests/App/ZoneManager/ZoneManagerProcessor.test.swift Tests beacon-exit processing.
Tests/App/ZoneManager/ZoneManagerCollector.test.swift Adds ranging and lifecycle regression coverage.
Tests/App/ZoneManager/ZoneManager.test.swift Updates collector test doubles.
Tests/App/ZoneManager/FakeCLLocationManager.swift Adds ranging and authorization test hooks.
Sources/Shared/API/Models/AppZone.swift Clarifies beacon monitoring behavior.
Sources/App/ZoneManager/ZoneManagerState.swift Adds ranging failure state.
Sources/App/ZoneManager/ZoneManagerRegionFilter.swift Enforces combined region-limit filtering.
Sources/App/ZoneManager/ZoneManagerProcessor.swift Processes beacon exits.
Sources/App/ZoneManager/ZoneManagerIgnoreReason.swift Updates verification ignore reason.
Sources/App/ZoneManager/ZoneManagerEvent.swift Adds ranging diagnostics.
Sources/App/ZoneManager/ZoneManagerCollector.swift Implements bounded ranging and reconciliation.
Sources/App/ZoneManager/UIApplicationBeaconScanBackgroundExecution.swift Implements background-task leasing.
Sources/App/ZoneManager/RangedBeaconSample.swift Defines ranged sample data.
Sources/App/ZoneManager/BeaconScanBackgroundExecution.swift Defines the execution abstraction.
Sources/App/ZoneManager/BeaconDiagnostic.swift Defines ranging diagnostic metadata.
Review details

Suppressed comments (3)

Sources/App/ZoneManager/ZoneManagerCollector.swift:166

  • This guard is global to the whole opportunistic scan: if any foreground entry (for example beacon A) exists, regions that are only opportunistic (beacon B) are never added or ranged. Let the constraint-sharing logic below reuse overlapping foreground ranges while still registering and starting new constraints.
        // Foreground ranging is already continuous, so an additional timed scan
        // would only duplicate work.
        guard foregroundBeaconEntries.isEmpty else { return }

Sources/App/ZoneManager/ZoneManagerCollector.swift:347

  • Every location update calls startOpportunisticBeaconScanning, whose first action cancels the current timeout and replaces it. Repeated location callbacks during one scan can therefore keep ranging and the background work alive past the intended 25-second bound and continually restart unchanged scans. Route this through the idempotent background reconciliation path or avoid replacing an active window when the regions are unchanged.
        startOpportunisticBeaconScanning(in: manager.monitoredRegions, manager: manager)

Sources/App/ZoneManager/ZoneManagerCollector.swift:98

  • When an existing identifier changes its beacon constraint, this removes the foreground entry but leaves any pending verification keyed only by that identifier. A callback for the old constraint can then be accepted and emitted as an event for the current zone even though its beacon definition changed. Invalidate pending work when the constraint changes, while preserving it only for an intentional lifecycle handoff to another consumer.
        let identifiersToRemove = foregroundBeaconEntries.compactMap { identifier, entry in
            desiredEntries[identifier]?.constraint == entry.constraint ? nil : identifier
        }
        let removedConstraints = identifiersToRemove.compactMap { identifier in
            foregroundBeaconEntries.removeValue(forKey: identifier)?.constraint
        }
  • Files reviewed: 17/17 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Sources/App/ZoneManager/UIApplicationBeaconScanBackgroundExecution.swift Outdated
Comment thread Sources/App/ZoneManager/ZoneManagerCollector.swift Outdated
@bgoncal
bgoncal requested a balanced review from Copilot September 11, 2026 11:12
@Pixelobserver Pixelobserver changed the title Improve bounded iBeacon ranging and region reconciliation Improve bounded iBeacon ranging and region reconciliation - 3 Sep 11, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Invalid background-task acquisition and non-empty samples being treated as empty can undermine bounded ranging correctness.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment on lines +9 to +15
identifier = UIApplication.shared.beginBackgroundTask(
withName: "ZoneManagerBeaconScan",
expirationHandler: { [weak self] in
expirationHandler()
self?.end()
}
)
Comment on lines +320 to +322
guard let detectedBeacon = samples.first(where: isBeaconInsideRange) else {
reconcileEmptyBeaconSample(identifiers: Array(identifiers), manager: manager)
return
@codecov

codecov Bot commented Sep 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.32258% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.93%. Comparing base (8815d0a) to head (881f43f).

Files with missing lines Patch % Lines
Sources/App/ZoneManager/ZoneManagerCollector.swift 93.93% 23 Missing ⚠️
...r/UIApplicationBeaconScanBackgroundExecution.swift 0.00% 14 Missing ⚠️
...rces/App/ZoneManager/ZoneManagerIgnoreReason.swift 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5735      +/-   ##
==========================================
+ Coverage   42.87%   42.93%   +0.06%     
==========================================
  Files        1124     1125       +1     
  Lines       78186    78286     +100     
==========================================
+ Hits        33523    33614      +91     
- Misses      44663    44672       +9     
Files with missing lines Coverage Δ
Sources/App/ZoneManager/ZoneManagerEvent.swift 98.36% <100.00%> (+0.02%) ⬆️
Sources/App/ZoneManager/ZoneManagerProcessor.swift 100.00% <ø> (ø)
...rces/App/ZoneManager/ZoneManagerRegionFilter.swift 97.82% <100.00%> (+0.09%) ⬆️
Sources/Shared/API/Models/AppZone.swift 75.40% <ø> (-0.30%) ⬇️
...rces/App/ZoneManager/ZoneManagerIgnoreReason.swift 0.00% <0.00%> (ø)
...r/UIApplicationBeaconScanBackgroundExecution.swift 0.00% <0.00%> (ø)
Sources/App/ZoneManager/ZoneManagerCollector.swift 93.96% <93.93%> (-6.04%) ⬇️

... and 46 files with indirect coverage changes

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bgoncal
bgoncal marked this pull request as draft September 11, 2026 13:45
@bgoncal

bgoncal commented Sep 11, 2026

Copy link
Copy Markdown
Member

Keeping as draft as we review the PRs that this one depends on

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants