Reconnect at once when the Mac wakes or its network changes
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import Foundation
|
||||
import Network
|
||||
import SwiftUI
|
||||
|
||||
private let staleAfter: TimeInterval = 40
|
||||
@@ -26,8 +25,7 @@ final class EventStream {
|
||||
private var retry: Task<Void, Never>?
|
||||
private var checker: Task<Void, Never>?
|
||||
private var generation = 0
|
||||
private let monitor = NWPathMonitor()
|
||||
private var online: Bool?
|
||||
private var networkObserver: (any NSObjectProtocol)?
|
||||
private var activeObserver: NotificationCenter.ObservationToken?
|
||||
|
||||
init(api: API, after: Int64, lead: Bool, onEvent: @escaping (ByreEvent) -> Void, onState: @escaping (Bool) -> Void, onReady: @escaping () -> Void) {
|
||||
@@ -43,13 +41,9 @@ final class EventStream {
|
||||
self?.check()
|
||||
}
|
||||
}
|
||||
monitor.pathUpdateHandler = { [weak self] path in
|
||||
let up = path.status == .satisfied
|
||||
Task { @MainActor in
|
||||
self?.pathChanged(up)
|
||||
networkObserver = NetworkWatch.shared.observe { [weak self] in
|
||||
self?.connect()
|
||||
}
|
||||
}
|
||||
monitor.start(queue: .main)
|
||||
#if os(macOS)
|
||||
activeObserver = NotificationCenter.default.addObserver(of: NSApplication.self, for: .didBecomeActive) { [weak self] _ in
|
||||
self?.check()
|
||||
@@ -67,21 +61,16 @@ final class EventStream {
|
||||
checker?.cancel()
|
||||
retry?.cancel()
|
||||
reading?.cancel()
|
||||
monitor.cancel()
|
||||
if let networkObserver {
|
||||
NotificationCenter.default.removeObserver(networkObserver)
|
||||
}
|
||||
networkObserver = nil
|
||||
if let activeObserver {
|
||||
NotificationCenter.default.removeObserver(activeObserver)
|
||||
}
|
||||
activeObserver = nil
|
||||
}
|
||||
|
||||
private func pathChanged(_ up: Bool) {
|
||||
let was = online
|
||||
online = up
|
||||
if was == false && up {
|
||||
connect()
|
||||
}
|
||||
}
|
||||
|
||||
private func check() {
|
||||
if Date().timeIntervalSince(heard) < staleAfter {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import Foundation
|
||||
import Network
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
private let networkChanged = Notification.Name("run.fc.byre.networkChanged")
|
||||
|
||||
@MainActor
|
||||
final class NetworkWatch {
|
||||
static let shared = NetworkWatch()
|
||||
|
||||
private let monitor = NWPathMonitor()
|
||||
private var seen = false
|
||||
|
||||
private init() {
|
||||
monitor.pathUpdateHandler = { [weak self] path in
|
||||
let summary = "\(path.status) \(path.availableInterfaces.map(\.name).joined(separator: " "))"
|
||||
Task { @MainActor in
|
||||
self?.pathChanged(summary)
|
||||
}
|
||||
}
|
||||
monitor.start(queue: .main)
|
||||
#if os(macOS)
|
||||
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.didWakeNotification, object: nil, queue: .main) { [weak self] _ in
|
||||
Task { @MainActor in
|
||||
log.notice("woke from sleep, reconnecting")
|
||||
self?.post()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
func observe(_ handler: @escaping @MainActor () -> Void) -> any NSObjectProtocol {
|
||||
NotificationCenter.default.addObserver(forName: networkChanged, object: self, queue: .main) { _ in
|
||||
MainActor.assumeIsolated {
|
||||
handler()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func pathChanged(_ summary: String) {
|
||||
guard seen else {
|
||||
seen = true
|
||||
return
|
||||
}
|
||||
log.notice("network changed (\(summary, privacy: .public)), reconnecting")
|
||||
post()
|
||||
}
|
||||
|
||||
private func post() {
|
||||
NotificationCenter.default.post(name: networkChanged, object: self)
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ final class Remote {
|
||||
private var stopped = false
|
||||
private var heard = Date()
|
||||
private var checker: Task<Void, Never>?
|
||||
private var networkObserver: (any NSObjectProtocol)?
|
||||
|
||||
init(api: API) {
|
||||
self.api = api
|
||||
@@ -69,16 +70,30 @@ final class Remote {
|
||||
self?.check()
|
||||
}
|
||||
}
|
||||
networkObserver = NetworkWatch.shared.observe { [weak self] in
|
||||
self?.restart()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
stopped = true
|
||||
checker?.cancel()
|
||||
if let networkObserver {
|
||||
NotificationCenter.default.removeObserver(networkObserver)
|
||||
}
|
||||
networkObserver = nil
|
||||
ws?.cancel(with: .normalClosure, reason: nil)
|
||||
ws = nil
|
||||
}
|
||||
|
||||
private func restart() {
|
||||
let old = ws
|
||||
ws = nil
|
||||
old?.cancel(with: .goingAway, reason: nil)
|
||||
start()
|
||||
}
|
||||
|
||||
// A socket byre closed while this Mac slept stays open here until something is sent on it.
|
||||
private func check() {
|
||||
guard let task = ws else {
|
||||
@@ -122,11 +137,13 @@ final class Remote {
|
||||
log.notice("client connection closed:\(errorText(error), privacy: .public)")
|
||||
self.ws = nil
|
||||
try? await Task.sleep(for: retryAfter)
|
||||
if self.ws == nil {
|
||||
self.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handle(_ task: URLSessionWebSocketTask, _ data: Data) {
|
||||
let req: ClientRequest
|
||||
|
||||
Reference in New Issue
Block a user