Meshtastic-Apple/Meshtastic/Accessory/Helpers/AsyncGate.swift
Garth Vander Houwen 6bb880d503
Two Column Node List (#1425)
* Restore BLE State

* Log privacy

* AccessoryManager to handle restored connection

* Comment task out

* Switch the node list to a two column layout

* Keep asian translations of channel details string

* Update restore state function based on conversation with jake

* Update Meshtastic/Accessory/Transports/Bluetooth Low Energy/BLETransport.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update Meshtastic/Accessory/Transports/Bluetooth Low Energy/BLETransport.swift

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* always show node list search bar

* Update auto correct modifier

* Dont show online animations for ios 17, remove online animation from node map, remove online circle from position popover

* Work in progress.

* Update detents

* Gate the discovery process while restoring

* Use geometry reader to size weather tiles on node details

* Update BLE Transport

* Update location weather condistion styles

* Log privacy in didReceive

* Remove extra dividers from admin key config, fix onboarding typo

* Bump minimum catalyst target

* Bump mac target version

* Use @FetchRequest for UserList to try and use less memory on ios 17

* Revert change to @fetchrequest

* Stab in the dark for Devices crash

* Updated UserList (back?) to @FetchRequest

* Set mac minimum to 15

* Nil out continuation after use

* Use @FetchRequest for the node list to stop crashes on iOS 17

* Handle failed connections during restoration

---------

Co-authored-by: Jake-B <jake-b@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-24 20:55:42 -07:00

74 lines
1.5 KiB
Swift

//
// AsyncGate.swift
// Meshtastic
//
// Created by Jake on 8/20/25.
//
import Foundation
actor AsyncGate {
private var waiters: [UUID: CheckedContinuation<Void, Error>] = [:]
private var isOpen = false
/// Wait until the gate is opened. Respects cancellation.
func wait() async throws {
if isOpen { return }
let id = UUID()
try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
waiters[id] = cont
}
} onCancel: {
Task { [weak self] in
await self?.cancelWaiter(id: id)
}
}
}
/// Opens the gate, resuming all current waiters.
func open() {
isOpen = true
for (_, cont) in waiters {
cont.resume()
}
waiters.removeAll()
}
/// Cancels all current waiters with `CancellationError`.
func cancelAll() {
for (_, cont) in waiters {
cont.resume(throwing: CancellationError())
}
waiters.removeAll()
}
/// Fails all current waiters with the provided error.
/// - Parameter error: The error to throw to all waiters.
func throwAll(_ error: Error) {
for (_, cont) in waiters {
cont.resume(throwing: error)
}
waiters.removeAll()
}
/// Alias for `throwAll(_:)` for readability.
func fail(_ error: Error) {
throwAll(error)
}
/// Resets the gate back to closed.
/// Future waiters will suspend again until `open()` is called.
func reset() {
isOpen = false
}
private func cancelWaiter(id: UUID) {
if let cont = waiters.removeValue(forKey: id) {
cont.resume(throwing: CancellationError())
}
}
}