From 10a6af488897030001f2c11055b37c10333fb028 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Wed, 16 Mar 2022 10:26:43 +0100 Subject: [PATCH 1/5] [connectivity_plus] Add NWPathMonitor based ConnectivityProvider for iOS * copied from MacOS implementation * available on iOS 12+ * correctly notifies when switching between Wifi networks without internet access --- .../connectivity_plus/CHANGELOG.md | 4 ++ .../PathMonitorConnectivityProvider.swift | 40 +++++++++++++++++++ .../Classes/SwiftConnectivityPlusPlugin.swift | 7 +++- .../connectivity_plus/pubspec.yaml | 2 +- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift diff --git a/packages/connectivity_plus/connectivity_plus/CHANGELOG.md b/packages/connectivity_plus/connectivity_plus/CHANGELOG.md index 1e1df9b8f1..2d028ac494 100644 --- a/packages/connectivity_plus/connectivity_plus/CHANGELOG.md +++ b/packages/connectivity_plus/connectivity_plus/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +- Add iOS ConnectivityProvider based on NWPathMonitor for iOS 12+. + ## 2.2.2 - Reachability.swift ".unavailable" for iOS is deprecated. diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift new file mode 100644 index 0000000000..b160f928a6 --- /dev/null +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift @@ -0,0 +1,40 @@ +import Foundation +import Network + +@available(iOS 12, *) +public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { + private let pathMonitor = NWPathMonitor() + + public var currentConnectivityType: ConnectivityType { + let path = pathMonitor.currentPath + if path.status == .satisfied { + if path.usesInterfaceType(.wifi) { + return .wifi + } else if path.usesInterfaceType(.cellular) { + return .cellular + } else if path.usesInterfaceType(.wiredEthernet) { + return .wiredEthernet + } + } + return .none + } + + public var connectivityUpdateHandler: ConnectivityUpdateHandler? + + override init() { + super.init() + pathMonitor.pathUpdateHandler = pathUpdateHandler + } + + public func start() { + pathMonitor.start(queue: .main) + } + + public func stop() { + pathMonitor.cancel() + } + + private func pathUpdateHandler(path: NWPath) { + connectivityUpdateHandler?(currentConnectivityType) + } +} diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift index 3908f5cbe0..c566902819 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/SwiftConnectivityPlusPlugin.swift @@ -23,7 +23,12 @@ public class SwiftConnectivityPlusPlugin: NSObject, FlutterPlugin, FlutterStream name: "dev.fluttercommunity.plus/connectivity_status", binaryMessenger: registrar.messenger()) - let connectivityProvider = ReachabilityConnectivityProvider() + let connectivityProvider: ConnectivityProvider + if #available(iOS 12, *) { + connectivityProvider = PathMonitorConnectivityProvider() + } else { + connectivityProvider = ReachabilityConnectivityProvider() + } let instance = SwiftConnectivityPlusPlugin(connectivityProvider: connectivityProvider) streamChannel.setStreamHandler(instance) diff --git a/packages/connectivity_plus/connectivity_plus/pubspec.yaml b/packages/connectivity_plus/connectivity_plus/pubspec.yaml index f2438e3de5..a95dc58724 100644 --- a/packages/connectivity_plus/connectivity_plus/pubspec.yaml +++ b/packages/connectivity_plus/connectivity_plus/pubspec.yaml @@ -1,6 +1,6 @@ name: connectivity_plus description: Flutter plugin for discovering the state of the network (WiFi & mobile/cellular) connectivity on Android and iOS. -version: 2.2.2 +version: 2.3.0 homepage: https://plus.fluttercommunity.dev/ repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/ From 2d7d6e2af7e6af2c0eb3aed61990b31755d25f2e Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Wed, 16 Mar 2022 15:36:16 +0100 Subject: [PATCH 2/5] [connectivity_plus] Fix NWPathMonitor can not be reused after stop() * noticeable mainly after hot restart --- .../ios/Classes/PathMonitorConnectivityProvider.swift | 10 +++++++++- .../connectivity_plus_macos/CHANGELOG.md | 4 ++++ .../Classes/PathMonitorConnectivityProvider.swift | 10 +++++++++- .../connectivity_plus_macos/pubspec.yaml | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift index b160f928a6..c3ec8b07b4 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift @@ -3,7 +3,14 @@ import Network @available(iOS 12, *) public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { - private let pathMonitor = NWPathMonitor() + private var pathMonitor: NWPathMonitor { + if (_pathMonitor == nil) { + _pathMonitor = NWPathMonitor() + } + return _pathMonitor! + } + + private var _pathMonitor:NWPathMonitor? public var currentConnectivityType: ConnectivityType { let path = pathMonitor.currentPath @@ -32,6 +39,7 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { public func stop() { pathMonitor.cancel() + _pathMonitor = nil } private func pathUpdateHandler(path: NWPath) { diff --git a/packages/connectivity_plus/connectivity_plus_macos/CHANGELOG.md b/packages/connectivity_plus/connectivity_plus_macos/CHANGELOG.md index ff79ff1a0a..143043e893 100644 --- a/packages/connectivity_plus/connectivity_plus_macos/CHANGELOG.md +++ b/packages/connectivity_plus/connectivity_plus_macos/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.2 + +- Fix connectivity stream can not be reused (after hot-restart) on MacOS 10.14+. + ## 1.2.1 - Update license headers. diff --git a/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift index 4f9e0e0117..e1046f103d 100644 --- a/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift @@ -3,7 +3,14 @@ import Network @available(macOS 10.14, *) public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { - private let pathMonitor = NWPathMonitor() + private let pathMonitor: NWPathMonitor + + private var pathMonitor: NWPathMonitor { + if (_pathMonitor == nil) { + _pathMonitor = NWPathMonitor() + } + return _pathMonitor! + } public var currentConnectivityType: ConnectivityType { let path = pathMonitor.currentPath @@ -32,6 +39,7 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { public func stop() { pathMonitor.cancel() + _pathMonitor = nil } private func pathUpdateHandler(path: NWPath) { diff --git a/packages/connectivity_plus/connectivity_plus_macos/pubspec.yaml b/packages/connectivity_plus/connectivity_plus_macos/pubspec.yaml index 60d33145e5..f6cbd36807 100644 --- a/packages/connectivity_plus/connectivity_plus_macos/pubspec.yaml +++ b/packages/connectivity_plus/connectivity_plus_macos/pubspec.yaml @@ -1,6 +1,6 @@ name: connectivity_plus_macos description: macOS implementation of the connectivity_plus plugin. -version: 1.2.1 +version: 1.2.2 homepage: https://plus.fluttercommunity.dev/ repository: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/ From d7b83c425f63a049d5195df99848c33a9eb1e894 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Wed, 16 Mar 2022 18:24:39 +0100 Subject: [PATCH 3/5] [connectivity_plus] Fix inconsistent initial status and stream behavior * ensure early initialization of backing `Reachability`/`NWPathMonitor` resulting in `checkConnectivity()` returning the correct status right away (until now it almost always returned `.none`) * having the correct initial status results in the the status stream not emitting double events on startup (previously most of the time it emitted 2 events starting with `.none` following the correct status) * the behavior is still incorrect after a hot restart because the backing `Reachability`/`NWPathMonitor` has been cleaned up and is not initialized early --- .../PathMonitorConnectivityProvider.swift | 28 +++++++++++------- .../ReachabilityConnectivityProvider.swift | 29 ++++++++++++++----- .../PathMonitorConnectivityProvider.swift | 28 +++++++++++------- .../ReachabilityConnectivityProvider.swift | 29 ++++++++++++++----- 4 files changed, 76 insertions(+), 38 deletions(-) diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift index c3ec8b07b4..c1949d43c9 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift @@ -3,17 +3,13 @@ import Network @available(iOS 12, *) public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { - private var pathMonitor: NWPathMonitor { - if (_pathMonitor == nil) { - _pathMonitor = NWPathMonitor() - } - return _pathMonitor! - } - private var _pathMonitor:NWPathMonitor? + private let queue = DispatchQueue.global(qos: .background) + + private var _pathMonitor: NWPathMonitor? public var currentConnectivityType: ConnectivityType { - let path = pathMonitor.currentPath + let path = ensurePathMonitor().currentPath if path.status == .satisfied { if path.usesInterfaceType(.wifi) { return .wifi @@ -30,18 +26,28 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { override init() { super.init() - pathMonitor.pathUpdateHandler = pathUpdateHandler + ensurePathMonitor() } public func start() { - pathMonitor.start(queue: .main) + ensurePathMonitor() } public func stop() { - pathMonitor.cancel() + _pathMonitor?.cancel() _pathMonitor = nil } + private func ensurePathMonitor() -> NWPathMonitor { + if (_pathMonitor == nil) { + let pathMonitor = NWPathMonitor() + pathMonitor.start(queue: queue) + pathMonitor.pathUpdateHandler = pathUpdateHandler + _pathMonitor = pathMonitor + } + return _pathMonitor! + } + private func pathUpdateHandler(path: NWPath) { connectivityUpdateHandler?(currentConnectivityType) } diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift index b10000c13a..ae189644b6 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/ReachabilityConnectivityProvider.swift @@ -2,11 +2,11 @@ import Foundation import Reachability public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { - private var reachability: Reachability? + private var _reachability: Reachability? public var currentConnectivityType: ConnectivityType { - let reachability = try? self.reachability ?? Reachability() - switch reachability?.connection { + let reachability = ensureReachability() + switch reachability.connection { case .wifi: return .wifi case .cellular: @@ -18,8 +18,13 @@ public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { public var connectivityUpdateHandler: ConnectivityUpdateHandler? + override init() { + super.init() + ensureReachability() + } + public func start() { - reachability = try? Reachability() + let reachability = ensureReachability() NotificationCenter.default.addObserver( self, @@ -27,17 +32,25 @@ public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { name: .reachabilityChanged, object: reachability) - try? reachability?.startNotifier() + try? reachability.startNotifier() } public func stop() { NotificationCenter.default.removeObserver( self, name: .reachabilityChanged, - object: reachability) + object: _reachability) - reachability?.stopNotifier() - reachability = nil + _reachability?.stopNotifier() + _reachability = nil + } + + private func ensureReachability() -> Reachability { + if (_reachability == nil) { + let reachability = try? Reachability() + _reachability = reachability + } + return _reachability! } @objc private func reachabilityChanged(notification: NSNotification) { diff --git a/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift index e1046f103d..8b96ae0f80 100644 --- a/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/PathMonitorConnectivityProvider.swift @@ -3,17 +3,13 @@ import Network @available(macOS 10.14, *) public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { - private let pathMonitor: NWPathMonitor - private var pathMonitor: NWPathMonitor { - if (_pathMonitor == nil) { - _pathMonitor = NWPathMonitor() - } - return _pathMonitor! - } + private let queue = DispatchQueue.global(qos: .background) + + private var _pathMonitor: NWPathMonitor? public var currentConnectivityType: ConnectivityType { - let path = pathMonitor.currentPath + let path = ensurePathMonitor().currentPath if path.status == .satisfied { if path.usesInterfaceType(.wifi) { return .wifi @@ -30,18 +26,28 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { override init() { super.init() - pathMonitor.pathUpdateHandler = pathUpdateHandler + ensurePathMonitor() } public func start() { - pathMonitor.start(queue: .main) + ensurePathMonitor() } public func stop() { - pathMonitor.cancel() + _pathMonitor?.cancel() _pathMonitor = nil } + private func ensurePathMonitor() -> NWPathMonitor { + if (_pathMonitor == nil) { + let pathMonitor = NWPathMonitor() + pathMonitor.start(queue: queue) + pathMonitor.pathUpdateHandler = pathUpdateHandler + _pathMonitor = pathMonitor + } + return _pathMonitor! + } + private func pathUpdateHandler(path: NWPath) { connectivityUpdateHandler?(currentConnectivityType) } diff --git a/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/ReachabilityConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/ReachabilityConnectivityProvider.swift index 72bb0f19c5..df7d36caa4 100644 --- a/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/ReachabilityConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus_macos/macos/Classes/ReachabilityConnectivityProvider.swift @@ -2,11 +2,11 @@ import Foundation import Reachability public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { - private var reachability: Reachability? + private var _reachability: Reachability? public var currentConnectivityType: ConnectivityType { - let reachability = try? self.reachability ?? Reachability() - switch reachability?.connection ?? .unavailable { + let reachability = ensureReachability() + switch reachability.connection ?? .unavailable { case .wifi: return .wifi case .cellular: @@ -18,8 +18,13 @@ public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { public var connectivityUpdateHandler: ConnectivityUpdateHandler? + override init() { + super.init() + ensureReachability() + } + public func start() { - reachability = try? Reachability() + let reachability = ensureReachability() NotificationCenter.default.addObserver( self, @@ -27,17 +32,25 @@ public class ReachabilityConnectivityProvider: NSObject, ConnectivityProvider { name: .reachabilityChanged, object: reachability) - try? reachability?.startNotifier() + try? reachability.startNotifier() } public func stop() { NotificationCenter.default.removeObserver( self, name: .reachabilityChanged, - object: reachability) + object: _reachability) - reachability?.stopNotifier() - reachability = nil + _reachability?.stopNotifier() + _reachability = nil + } + + private func ensureReachability() -> Reachability { + if (_reachability == nil) { + let reachability = try? Reachability() + _reachability = reachability + } + return _reachability! } @objc private func reachabilityChanged(notification: NSNotification) { From c8235da423c1cefd7ceba5937ee5f483a8b12f25 Mon Sep 17 00:00:00 2001 From: Peter Leibiger Date: Wed, 16 Mar 2022 18:46:35 +0100 Subject: [PATCH 4/5] [connectivity_plus] Report correct status on iOS simulator --- .../ios/Classes/PathMonitorConnectivityProvider.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift index c1949d43c9..40fe7d85a9 100644 --- a/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift +++ b/packages/connectivity_plus/connectivity_plus/ios/Classes/PathMonitorConnectivityProvider.swift @@ -16,7 +16,9 @@ public class PathMonitorConnectivityProvider: NSObject, ConnectivityProvider { } else if path.usesInterfaceType(.cellular) { return .cellular } else if path.usesInterfaceType(.wiredEthernet) { - return .wiredEthernet + // .wiredEthernet is available in simulator + // but for consistency it is probably correct to report .wifi + return .wifi } } return .none From 83fbb0e5fb773c1d75babf6090e5bfb2e74e920a Mon Sep 17 00:00:00 2001 From: Miquel Beltran Date: Tue, 12 Apr 2022 09:17:54 +0200 Subject: [PATCH 5/5] bump dependency to connectivity_plus_macos --- packages/connectivity_plus/connectivity_plus/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/connectivity_plus/connectivity_plus/pubspec.yaml b/packages/connectivity_plus/connectivity_plus/pubspec.yaml index a95dc58724..03f6f43662 100644 --- a/packages/connectivity_plus/connectivity_plus/pubspec.yaml +++ b/packages/connectivity_plus/connectivity_plus/pubspec.yaml @@ -30,7 +30,7 @@ dependencies: sdk: flutter connectivity_plus_platform_interface: ^1.2.0 connectivity_plus_linux: ^1.3.0 - connectivity_plus_macos: ^1.2.0 + connectivity_plus_macos: ^1.2.2 connectivity_plus_web: ^1.2.0 connectivity_plus_windows: ^1.2.0