Skip to content

Commit

Permalink
Merge pull request #4 from claucambra/feature/network-observation
Browse files Browse the repository at this point in the history
Implement network state observation and handling of connection changes in remote change observer
  • Loading branch information
claucambra authored Jul 10, 2024
2 parents b0eddd1 + 215ca29 commit d2cfed3
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,14 @@ public class RemoteChangeObserver: NSObject, NKCommonDelegate, URLSessionWebSock
}
public var pollingActive: Bool { pollingTimer != nil }

private var networkReachability: NKCommon.TypeReachability = .unknown {
private(set) var networkReachability: NKCommon.TypeReachability = .unknown {
didSet {
if oldValue == .notReachable, networkReachability != .notReachable {
if networkReachability == .notReachable {
logger.info("Network unreachable, stopping websocket and stopping polling")
stopPollingTimer()
resetWebSocket()
} else if oldValue == .notReachable {
logger.info("Network reachable, trying to reconnect to websocket")
reconnectWebSocket()
changeNotificationInterface.notifyChange()
}
Expand Down Expand Up @@ -364,4 +369,8 @@ public class RemoteChangeObserver: NSObject, NKCommonDelegate, URLSessionWebSock
)
}
}

public func networkReachabilityObserver(_ typeReachability: NKCommon.TypeReachability) {
networkReachability = typeReachability
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -333,4 +333,87 @@ final class RemoteChangeObserverTests: XCTestCase {
try await Task.sleep(nanoseconds: UInt64(intendedPingsWait))
XCTAssertEqual(pings, intendedPings)
}

func testRetryOnConnectionLoss() async throws {
let remoteInterface = MockRemoteInterface(account: Self.account)
remoteInterface.capabilities = mockCapabilities

var authenticated = false
var notified = false

NotificationCenter.default.addObserver(
forName: NotifyPushAuthenticatedNotificationName, object: nil, queue: nil
) { _ in
authenticated = true
}

let notificationInterface = MockChangeNotificationInterface()
notificationInterface.changeHandler = { notified = true }
remoteChangeObserver = RemoteChangeObserver(
remoteInterface: remoteInterface,
changeNotificationInterface: notificationInterface,
domain: nil
)
remoteChangeObserver?.networkReachabilityObserver(.reachableEthernetOrWiFi)

for _ in 0...Self.timeout {
try await Task.sleep(nanoseconds: 1_000_000)
if authenticated {
break
}
}
XCTAssertTrue(authenticated)

Self.notifyPushServer.send(message: "notify_file")
for _ in 0...Self.timeout {
try await Task.sleep(nanoseconds: 1_000_000)
if notified {
break
}
}
XCTAssertTrue(notified) // Check notification handling is working properly

remoteChangeObserver?.networkReachabilityObserver(.notReachable)
Self.notifyPushServer.resetCredentialsState()
authenticated = false
for _ in 0...Self.timeout {
try await Task.sleep(nanoseconds: 1_000_000)
if authenticated {
break
}
}
// Should still be false. The mock notify push server is still online so if we the
// remote change observer attempts to connect it _will_ be correctly authentiated,
// but once we have set the network reachability to unreachable it shouldn't be
// trying to connect at all.
XCTAssertFalse(authenticated)

notified = false
Self.notifyPushServer.send(message: "notify_file")
for _ in 0...Self.timeout {
try await Task.sleep(nanoseconds: 1_000_000)
if notified {
break
}
}
XCTAssertFalse(notified) // Check we disconnected and are not listening to the server

remoteChangeObserver?.networkReachabilityObserver(.reachableEthernetOrWiFi)
for _ in 0...Self.timeout {
try await Task.sleep(nanoseconds: 1_000_000)
if authenticated {
break
}
}
XCTAssertTrue(authenticated)

Self.notifyPushServer.send(message: "notify_file")
for _ in 0...Self.timeout {
try await Task.sleep(nanoseconds: 1_000_000)
if notified {
break
}
}
XCTAssertTrue(notified) // Check notification handling is working properly again
}
}

0 comments on commit d2cfed3

Please sign in to comment.