Compare commits
2 Commits
main
...
feat/conne
Author | SHA1 | Date |
---|---|---|
![]() |
aefe583213 | |
![]() |
bf25b04f9d |
|
@ -10,7 +10,7 @@
|
||||||
--elseposition same-line
|
--elseposition same-line
|
||||||
|
|
||||||
--enable fileHeader
|
--enable fileHeader
|
||||||
--header "//\n// Copyright 2018-{created.year} Amazon.com,\n// Inc. or its affiliates. All Rights Reserved.\n//\n// SPDX-License-Identifier: Apache-2.0\n//"
|
--header "//\n// Copyright 2018-2021 Amazon.com,\n// Inc. or its affiliates. All Rights Reserved.\n//\n// SPDX-License-Identifier: Apache-2.0\n//"
|
||||||
|
|
||||||
--disable hoistPatternLet
|
--disable hoistPatternLet
|
||||||
--patternlet inline
|
--patternlet inline
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */; };
|
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */; };
|
||||||
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */; };
|
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */; };
|
||||||
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */; };
|
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */; };
|
||||||
|
219BFF3B27A38902000FC148 /* schema.graphql in Resources */ = {isa = PBXBuildFile; fileRef = 219BFF3A27A38902000FC148 /* schema.graphql */; };
|
||||||
|
219BFF4927A3B238000FC148 /* ConnectivityMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219BFF4827A3B237000FC148 /* ConnectivityMonitor.swift */; };
|
||||||
|
219BFF4B27A3B24F000FC148 /* ConnectivityPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219BFF4A27A3B24F000FC148 /* ConnectivityPath.swift */; };
|
||||||
21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */; };
|
21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */; };
|
||||||
21D38B432409AFBD00EC2A8D /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; };
|
21D38B432409AFBD00EC2A8D /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; };
|
||||||
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; };
|
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; };
|
||||||
|
@ -159,6 +162,9 @@
|
||||||
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockConnectionProvider.swift; sourceTree = "<group>"; };
|
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockConnectionProvider.swift; sourceTree = "<group>"; };
|
||||||
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProviderTests.swift; sourceTree = "<group>"; };
|
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProviderTests.swift; sourceTree = "<group>"; };
|
||||||
217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeGatewayURLInterceptorTests.swift; sourceTree = "<group>"; };
|
217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeGatewayURLInterceptorTests.swift; sourceTree = "<group>"; };
|
||||||
|
219BFF3A27A38902000FC148 /* schema.graphql */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = schema.graphql; sourceTree = "<group>"; };
|
||||||
|
219BFF4827A3B237000FC148 /* ConnectivityMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityMonitor.swift; sourceTree = "<group>"; };
|
||||||
|
219BFF4A27A3B24F000FC148 /* ConnectivityPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityPath.swift; sourceTree = "<group>"; };
|
||||||
21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientIntegrationTests.swift; sourceTree = "<group>"; };
|
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientIntegrationTests.swift; sourceTree = "<group>"; };
|
||||||
21D38B422409AFBD00EC2A8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
21D38B422409AFBD00EC2A8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
@ -292,9 +298,10 @@
|
||||||
217F39912405D9D500F1A0B3 /* AppSyncRealTimeClient */ = {
|
217F39912405D9D500F1A0B3 /* AppSyncRealTimeClient */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
217F39932405D9D500F1A0B3 /* Info.plist */,
|
|
||||||
217F39B82406E98300F1A0B3 /* Connection */,
|
217F39B82406E98300F1A0B3 /* Connection */,
|
||||||
217F39A92406E98300F1A0B3 /* ConnectionProvider */,
|
217F39A92406E98300F1A0B3 /* ConnectionProvider */,
|
||||||
|
219BFF4727A3B223000FC148 /* ConnectivityMonitor */,
|
||||||
|
217F39932405D9D500F1A0B3 /* Info.plist */,
|
||||||
21D38B6E240A272F00EC2A8D /* Interceptor */,
|
21D38B6E240A272F00EC2A8D /* Interceptor */,
|
||||||
217F39C62406E98400F1A0B3 /* Support */,
|
217F39C62406E98400F1A0B3 /* Support */,
|
||||||
217F39C12406E98400F1A0B3 /* Websocket */,
|
217F39C12406E98400F1A0B3 /* Websocket */,
|
||||||
|
@ -437,6 +444,15 @@
|
||||||
path = Support;
|
path = Support;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
219BFF4727A3B223000FC148 /* ConnectivityMonitor */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
219BFF4827A3B237000FC148 /* ConnectivityMonitor.swift */,
|
||||||
|
219BFF4A27A3B24F000FC148 /* ConnectivityPath.swift */,
|
||||||
|
);
|
||||||
|
path = ConnectivityMonitor;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
21D38B3F2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */ = {
|
21D38B3F2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -497,6 +513,7 @@
|
||||||
21D38B95240C4DC200EC2A8D /* Support */ = {
|
21D38B95240C4DC200EC2A8D /* Support */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
219BFF3A27A38902000FC148 /* schema.graphql */,
|
||||||
21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */,
|
21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */,
|
||||||
21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */,
|
21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */,
|
||||||
21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */,
|
21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */,
|
||||||
|
@ -753,6 +770,7 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
21D38B4E2409B8B200EC2A8D /* README.md in Resources */,
|
21D38B4E2409B8B200EC2A8D /* README.md in Resources */,
|
||||||
|
219BFF3B27A38902000FC148 /* schema.graphql in Resources */,
|
||||||
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */,
|
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -1103,6 +1121,7 @@
|
||||||
217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */,
|
217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */,
|
||||||
21D38B8E240A3C2300EC2A8D /* ConnectionProviderFactory.swift in Sources */,
|
21D38B8E240A3C2300EC2A8D /* ConnectionProviderFactory.swift in Sources */,
|
||||||
217F39E02406E98400F1A0B3 /* AppSyncWebsocketProvider.swift in Sources */,
|
217F39E02406E98400F1A0B3 /* AppSyncWebsocketProvider.swift in Sources */,
|
||||||
|
219BFF4927A3B238000FC148 /* ConnectivityMonitor.swift in Sources */,
|
||||||
FAB7E91224D2644E00DF1EA1 /* RealtimeConnectionProvider+StaleConnection.swift in Sources */,
|
FAB7E91224D2644E00DF1EA1 /* RealtimeConnectionProvider+StaleConnection.swift in Sources */,
|
||||||
217F39D32406E98400F1A0B3 /* RealtimeConnectionProvider.swift in Sources */,
|
217F39D32406E98400F1A0B3 /* RealtimeConnectionProvider.swift in Sources */,
|
||||||
217F39D12406E98400F1A0B3 /* AppSyncConnectionRequest.swift in Sources */,
|
217F39D12406E98400F1A0B3 /* AppSyncConnectionRequest.swift in Sources */,
|
||||||
|
@ -1126,6 +1145,7 @@
|
||||||
217F39D42406E98400F1A0B3 /* RealtimeConnectionProvider+Websocket.swift in Sources */,
|
217F39D42406E98400F1A0B3 /* RealtimeConnectionProvider+Websocket.swift in Sources */,
|
||||||
217F39DC2406E98400F1A0B3 /* AppSyncSubscriptionConnection.swift in Sources */,
|
217F39DC2406E98400F1A0B3 /* AppSyncSubscriptionConnection.swift in Sources */,
|
||||||
21D38B6D240A262800EC2A8D /* AppSyncJSONHelper.swift in Sources */,
|
21D38B6D240A262800EC2A8D /* AppSyncJSONHelper.swift in Sources */,
|
||||||
|
219BFF4B27A3B24F000FC148 /* ConnectivityPath.swift in Sources */,
|
||||||
217F39CE2406E98400F1A0B3 /* ConnectionProviderError.swift in Sources */,
|
217F39CE2406E98400F1A0B3 /* ConnectionProviderError.swift in Sources */,
|
||||||
217F39E12406E98400F1A0B3 /* StarscreamAdapter.swift in Sources */,
|
217F39E12406E98400F1A0B3 /* StarscreamAdapter.swift in Sources */,
|
||||||
217F39D72406E98400F1A0B3 /* RealtimeConnectionProvider+ConnectionInterceptable.swift in Sources */,
|
217F39D72406E98400F1A0B3 /* RealtimeConnectionProvider+ConnectionInterceptable.swift in Sources */,
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
enableThreadSanitizer = "YES"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -33,6 +33,28 @@ extension RealtimeConnectionProvider {
|
||||||
staleConnectionTimer?.resetCountdown()
|
staleConnectionTimer?.resetCountdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle updates from the ConnectivityMonitor
|
||||||
|
func handleConnectivityUpdates(connectivity: ConnectivityPath) {
|
||||||
|
connectionQueue.async {[weak self] in
|
||||||
|
guard let self = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
AppSyncLogger.debug("[RealtimeConnectionProvider] Status: \(self.status). Connectivity status: \(connectivity.status)")
|
||||||
|
if self.status == .connected && connectivity.status == .unsatisfied && !self.isStaleConnection {
|
||||||
|
AppSyncLogger.debug("[RealtimeConnectionProvider] Connetion is stale. Pending reconnect on connectivity.")
|
||||||
|
self.isStaleConnection = true
|
||||||
|
|
||||||
|
} else if self.status == .connected && self.isStaleConnection && connectivity.status == .satisfied {
|
||||||
|
AppSyncLogger.debug("[RealtimeConnectionProvider] Connetion is stale. Disconnecting to begin reconnect.")
|
||||||
|
if self.staleConnectionTimer != nil {
|
||||||
|
self.stopStaleConnectionTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.disconnectStaleConnection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Fired when the stale connection timer expires
|
/// Fired when the stale connection timer expires
|
||||||
private func disconnectStaleConnection() {
|
private func disconnectStaleConnection() {
|
||||||
connectionQueue.async {[weak self] in
|
connectionQueue.async {[weak self] in
|
||||||
|
@ -41,9 +63,9 @@ extension RealtimeConnectionProvider {
|
||||||
}
|
}
|
||||||
AppSyncLogger.error("[RealtimeConnectionProvider] Realtime connection is stale, disconnecting.")
|
AppSyncLogger.error("[RealtimeConnectionProvider] Realtime connection is stale, disconnecting.")
|
||||||
self.status = .notConnected
|
self.status = .notConnected
|
||||||
|
self.isStaleConnection = false
|
||||||
self.websocket.disconnect()
|
self.websocket.disconnect()
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.connection))
|
self.updateCallback(event: .error(ConnectionProviderError.connection))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,12 +28,18 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
/// alive" message will cause the timer to be reset to the full interval.
|
/// alive" message will cause the timer to be reset to the full interval.
|
||||||
var staleConnectionTimer: CountdownTimer?
|
var staleConnectionTimer: CountdownTimer?
|
||||||
|
|
||||||
|
/// Intermediate state when the connection is connected and connectivity updates to unsatisfied (offline)
|
||||||
|
var isStaleConnection: Bool
|
||||||
|
|
||||||
/// Manages concurrency for socket connections, disconnections, writes, and status reports.
|
/// Manages concurrency for socket connections, disconnections, writes, and status reports.
|
||||||
///
|
///
|
||||||
/// Each connection request will be sent to this queue. Connection request are
|
/// Each connection request will be sent to this queue. Connection request are
|
||||||
/// handled one at a time.
|
/// handled one at a time.
|
||||||
let connectionQueue: DispatchQueue
|
let connectionQueue: DispatchQueue
|
||||||
|
|
||||||
|
/// Monitor for connectivity updates to disconnect the current connection if it flips between connectivity statuses
|
||||||
|
let connectivityMonitor: ConnectivityMonitor
|
||||||
|
|
||||||
/// The serial queue on which status & message callbacks from the web socket are invoked.
|
/// The serial queue on which status & message callbacks from the web socket are invoked.
|
||||||
private let serialCallbackQueue = DispatchQueue(
|
private let serialCallbackQueue = DispatchQueue(
|
||||||
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.callbackQueue"
|
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.callbackQueue"
|
||||||
|
@ -51,6 +57,9 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
self.connectionQueue = DispatchQueue(
|
self.connectionQueue = DispatchQueue(
|
||||||
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.serialQueue"
|
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.serialQueue"
|
||||||
)
|
)
|
||||||
|
self.isStaleConnection = false
|
||||||
|
self.connectivityMonitor = ConnectivityMonitor()
|
||||||
|
connectivityMonitor.start(connectivityUpdates: handleConnectivityUpdates(connectivity:))
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ConnectionProvider methods
|
// MARK: - ConnectionProvider methods
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
//
|
||||||
|
// Copyright 2018-2021 Amazon.com,
|
||||||
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
|
||||||
|
typealias ConnectivityUpdates = (ConnectivityPath) -> Void
|
||||||
|
|
||||||
|
protocol AnyConnectivityMonitor {
|
||||||
|
func start(connectivityUpdatesQueue: DispatchQueue, onConnectivityUpdates: @escaping ConnectivityUpdates)
|
||||||
|
func cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConnectivityMonitor {
|
||||||
|
var connectivity: ConnectivityPath?
|
||||||
|
|
||||||
|
private let connectivityUpdatesQueue = DispatchQueue(
|
||||||
|
label: "com.amazonaws.ConnectivityMonitor.connectivityUpdatesQueue",
|
||||||
|
qos: .background
|
||||||
|
)
|
||||||
|
private var monitor: AnyConnectivityMonitor?
|
||||||
|
|
||||||
|
init(connectivityMonitor: AnyConnectivityMonitor? = nil) {
|
||||||
|
self.monitor = connectivityMonitor
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(connectivityUpdates: @escaping ConnectivityUpdates) {
|
||||||
|
if let monitor = monitor {
|
||||||
|
monitor.start(
|
||||||
|
connectivityUpdatesQueue: connectivityUpdatesQueue,
|
||||||
|
onConnectivityUpdates: connectivityUpdates
|
||||||
|
)
|
||||||
|
} else if #available(iOS 12.0, *) {
|
||||||
|
let monitor = NetworkMonitor()
|
||||||
|
self.monitor = monitor
|
||||||
|
monitor.start(
|
||||||
|
connectivityUpdatesQueue: connectivityUpdatesQueue,
|
||||||
|
onConnectivityUpdates: connectivityUpdates
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cancel() {
|
||||||
|
guard let monitor = monitor else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
monitor.cancel()
|
||||||
|
self.monitor = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 6.0, *)
|
||||||
|
class NetworkMonitor: AnyConnectivityMonitor {
|
||||||
|
private var monitor: NWPathMonitor?
|
||||||
|
private var onConnectivityUpdates: ConnectivityUpdates?
|
||||||
|
private var connectivityUpdatesQueue: DispatchQueue?
|
||||||
|
private let queue = DispatchQueue(label: "com.amazonaws.NetworkMonitor.queue", qos: .background)
|
||||||
|
|
||||||
|
func start(connectivityUpdatesQueue: DispatchQueue, onConnectivityUpdates: @escaping ConnectivityUpdates) {
|
||||||
|
self.connectivityUpdatesQueue = connectivityUpdatesQueue
|
||||||
|
self.onConnectivityUpdates = onConnectivityUpdates
|
||||||
|
// A new instance is required each time a monitor is started
|
||||||
|
let monitor = NWPathMonitor()
|
||||||
|
monitor.pathUpdateHandler = didUpdate(path:)
|
||||||
|
monitor.start(queue: queue)
|
||||||
|
self.monitor = monitor
|
||||||
|
}
|
||||||
|
|
||||||
|
func cancel() {
|
||||||
|
guard let monitor = monitor else { return }
|
||||||
|
defer {
|
||||||
|
self.monitor = nil
|
||||||
|
}
|
||||||
|
monitor.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func didUpdate(path: NWPath) {
|
||||||
|
guard let onConnectivityUpdates = onConnectivityUpdates,
|
||||||
|
let connectivityUpdatesQueue = connectivityUpdatesQueue else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let connectivityPath = ConnectivityPath(path: path)
|
||||||
|
connectivityUpdatesQueue.async {
|
||||||
|
onConnectivityUpdates(connectivityPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
//
|
||||||
|
// Copyright 2018-2021 Amazon.com,
|
||||||
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
|
||||||
|
struct ConnectivityPath {
|
||||||
|
let status: ConnectivityStatus
|
||||||
|
let availableInterfaces: [ConnectivityInterface]
|
||||||
|
let isExpensive: Bool
|
||||||
|
let supportsDNS: Bool
|
||||||
|
let supportsIPv4: Bool
|
||||||
|
let supportsIPv6: Bool
|
||||||
|
|
||||||
|
init(
|
||||||
|
status: ConnectivityStatus = .unsatisfied,
|
||||||
|
availableInterfaces: [ConnectivityInterface] = [],
|
||||||
|
isExpensive: Bool = false,
|
||||||
|
supportsDNS: Bool = false,
|
||||||
|
supportsIPv4: Bool = false,
|
||||||
|
supportsIPv6: Bool = false
|
||||||
|
) {
|
||||||
|
self.status = status
|
||||||
|
self.availableInterfaces = availableInterfaces
|
||||||
|
self.isExpensive = isExpensive
|
||||||
|
self.supportsDNS = supportsDNS
|
||||||
|
self.supportsIPv4 = supportsIPv4
|
||||||
|
self.supportsIPv6 = supportsIPv6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ConnectivityPath: CustomStringConvertible {
|
||||||
|
var description: String {
|
||||||
|
[
|
||||||
|
"\(status): \(availableInterfaces.description)",
|
||||||
|
"Expensive = \(isExpensive ? "YES" : "NO")",
|
||||||
|
"DNS = \(supportsDNS ? "YES" : "NO")",
|
||||||
|
"IPv4 = \(supportsIPv4 ? "YES" : "NO")",
|
||||||
|
"IPv6 = \(supportsIPv6 ? "YES" : "NO")"
|
||||||
|
].joined(separator: "; ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ConnectivityPath {
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
init(path: NWPath) {
|
||||||
|
self.status = ConnectivityStatus(status: path.status)
|
||||||
|
self.availableInterfaces = path.availableInterfaces.map { ConnectivityInterface(interface: $0) }
|
||||||
|
self.isExpensive = path.isExpensive
|
||||||
|
self.supportsDNS = path.supportsDNS
|
||||||
|
self.supportsIPv4 = path.supportsIPv4
|
||||||
|
self.supportsIPv6 = path.supportsIPv6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectivityInterfaceType: String {
|
||||||
|
case other
|
||||||
|
case wifi
|
||||||
|
case cellular
|
||||||
|
case wiredEthernet
|
||||||
|
case loopback
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ConnectivityInterfaceType {
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
init(interfaceType: NWInterface.InterfaceType) {
|
||||||
|
switch interfaceType {
|
||||||
|
case .other:
|
||||||
|
self = .other
|
||||||
|
case .wifi:
|
||||||
|
self = .wifi
|
||||||
|
case .cellular:
|
||||||
|
self = .cellular
|
||||||
|
case .wiredEthernet:
|
||||||
|
self = .wiredEthernet
|
||||||
|
case .loopback:
|
||||||
|
self = .loopback
|
||||||
|
@unknown default:
|
||||||
|
self = .other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ConnectivityInterface {
|
||||||
|
public let name: String
|
||||||
|
public let type: ConnectivityInterfaceType
|
||||||
|
|
||||||
|
public init(name: String, type: ConnectivityInterfaceType) {
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extension ConnectivityInterface {
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
init(interface: NWInterface) {
|
||||||
|
self.name = interface.name
|
||||||
|
self.type = ConnectivityInterfaceType(interfaceType: interface.type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectivityStatus: String {
|
||||||
|
case satisfied
|
||||||
|
case unsatisfied
|
||||||
|
case requiresConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ConnectivityStatus {
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
init(status: NWPath.Status) {
|
||||||
|
switch status {
|
||||||
|
case .satisfied:
|
||||||
|
self = .satisfied
|
||||||
|
case .unsatisfied:
|
||||||
|
self = .unsatisfied
|
||||||
|
case .requiresConnection:
|
||||||
|
self = .requiresConnection
|
||||||
|
@unknown default:
|
||||||
|
self = .unsatisfied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue