mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-27 17:58:47 +00:00
iOS does not report the sockets of the previous network as broken, so after switching between Wi-Fi and mobile the agent keeps using dead sessions - setting network info does not drop them. Recovery only happened when the OS finally failed the socket, via TCP keep-alive or the SMP ping monitor, taking from ~90 seconds to tens of minutes. Reconnect the servers on every network change after the first one, when the network is online. Changes that keep the same network type, e.g. switching between Wi-Fi networks, are not detected, as network info only has the network type.
89 lines
2.7 KiB
Swift
89 lines
2.7 KiB
Swift
//
|
|
// NetworkObserver.swift
|
|
// SimpleX (iOS)
|
|
//
|
|
// Created by Avently on 05.04.2024.
|
|
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import Network
|
|
import SimpleXChat
|
|
|
|
class NetworkObserver {
|
|
static let shared = NetworkObserver()
|
|
private let queue: DispatchQueue = DispatchQueue(label: "chat.simplex.app.NetworkObserver")
|
|
private var prevInfo: UserNetworkInfo? = nil
|
|
private var monitor: NWPathMonitor?
|
|
private let monitorLock: DispatchQueue = DispatchQueue(label: "chat.simplex.app.monitorLock")
|
|
|
|
func restartMonitor() {
|
|
monitorLock.sync {
|
|
monitor?.cancel()
|
|
let mon = NWPathMonitor()
|
|
mon.pathUpdateHandler = { [weak self] path in
|
|
self?.networkPathChanged(path: path)
|
|
}
|
|
mon.start(queue: queue)
|
|
monitor = mon
|
|
}
|
|
}
|
|
|
|
private func networkPathChanged(path: NWPath) {
|
|
let info = UserNetworkInfo(
|
|
networkType: networkTypeFromPath(path),
|
|
online: path.status == .satisfied
|
|
)
|
|
if (prevInfo != info) {
|
|
// iOS does not report the sockets of the previous network as broken, so the servers have to
|
|
// be reconnected. There is nothing to reconnect before the first network info is set.
|
|
let shouldReconnect = prevInfo != nil && info.online
|
|
prevInfo = info
|
|
setNetworkInfo(info)
|
|
if shouldReconnect { reconnectServers() }
|
|
}
|
|
}
|
|
|
|
private func networkTypeFromPath(_ path: NWPath) -> UserNetworkType {
|
|
if path.usesInterfaceType(.wiredEthernet) {
|
|
.ethernet
|
|
} else if path.usesInterfaceType(.wifi) {
|
|
.wifi
|
|
} else if path.usesInterfaceType(.cellular) {
|
|
.cellular
|
|
} else if path.usesInterfaceType(.other) {
|
|
.other
|
|
} else {
|
|
.none
|
|
}
|
|
}
|
|
|
|
private static var networkObserver: NetworkObserver? = nil
|
|
|
|
private func setNetworkInfo(_ info: UserNetworkInfo) {
|
|
logger.debug("setNetworkInfo Network changed: \(String(describing: info))")
|
|
DispatchQueue.main.sync {
|
|
ChatModel.shared.networkInfo = info
|
|
}
|
|
if !hasChatCtrl() { return }
|
|
self.monitorLock.sync {
|
|
do {
|
|
try apiSetNetworkInfo(info)
|
|
} catch let err {
|
|
logger.error("setNetworkInfo error: \(responseError(err))")
|
|
}
|
|
}
|
|
}
|
|
|
|
private func reconnectServers() {
|
|
Task {
|
|
if !hasChatCtrl() { return }
|
|
do {
|
|
try await reconnectAllServers()
|
|
} catch let err {
|
|
logger.error("reconnectAllServers error: \(responseError(err))")
|
|
}
|
|
}
|
|
}
|
|
}
|