Skip to content

Commit dabe790

Browse files
authored
Merge pull request #4986 from woocommerce/issue/4384-connectivity-service
Add new service for observing connectivity status
2 parents 3d003d5 + d947ba3 commit dabe790

File tree

4 files changed

+135
-0
lines changed

4 files changed

+135
-0
lines changed

WooCommerce/Classes/ServiceLocator/ServiceLocator.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ final class ServiceLocator {
7070
///
7171
private static var _receiptPrinter: PrinterService = AirPrintReceiptPrinterService()
7272

73+
/// Observer for network connectivity
74+
///
75+
private static var _connectivityObserver: ConnectivityObserver = DefaultConnectivityObserver()
76+
7377
// MARK: - Getters
7478

7579
/// Provides the access point to the analytics.
@@ -182,6 +186,12 @@ final class ServiceLocator {
182186
static var receiptPrinterService: PrinterService {
183187
_receiptPrinter
184188
}
189+
190+
/// Provides access point to the ConnectivityObserver.
191+
/// - Returns: An implementation of the ConnectivityObserver protocol.
192+
static var connectivityObserver: ConnectivityObserver {
193+
_connectivityObserver
194+
}
185195
}
186196

187197

@@ -292,6 +302,14 @@ extension ServiceLocator {
292302

293303
_receiptPrinter = mock
294304
}
305+
306+
static func setConnectivityObserver(_ mock: ConnectivityObserver) {
307+
guard isRunningTests() else {
308+
return
309+
}
310+
311+
_connectivityObserver = mock
312+
}
295313
}
296314

297315

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Foundation
2+
3+
/// Interface for the observing connectivity
4+
///
5+
protocol ConnectivityObserver {
6+
/// Getter for current state of the connectivity.
7+
var isConnectivityAvailable: Bool { get }
8+
9+
/// Starts the observer.
10+
func startObserving()
11+
12+
/// Updates the listener for the connectivity observer.
13+
func updateListener(_ listener: @escaping (ConnectivityStatus) -> Void)
14+
15+
/// Stops the observer.
16+
func stopObserving()
17+
}
18+
19+
/// Defines the various states of network connectivity.
20+
///
21+
/// - unknown: It is unknown whether the network is reachable.
22+
/// - notReachable: The network is not reachable.
23+
/// - reachable: The network is reachable.
24+
enum ConnectivityStatus: Equatable {
25+
case unknown
26+
case notReachable
27+
case reachable(type: ConnectionType)
28+
}
29+
30+
/// Defines the various connection types detected.
31+
///
32+
/// - ethernetOrWiFi: The connection type is either over Ethernet or WiFi.
33+
/// - cellular: The connection type is a cellular connection.
34+
/// - other: The connection type is via a local loopback network, virtual network or other unknown types.
35+
enum ConnectionType: Equatable {
36+
case ethernetOrWiFi
37+
case cellular
38+
case other
39+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import Foundation
2+
import Network
3+
4+
final class DefaultConnectivityObserver: ConnectivityObserver {
5+
6+
/// Network monitor to evaluate connection.
7+
///
8+
private let networkMonitor: NWPathMonitor
9+
private let observingQueue: DispatchQueue = .global(qos: .background)
10+
11+
var isConnectivityAvailable: Bool {
12+
if case .reachable = connectivityStatus(from: networkMonitor.currentPath) {
13+
return true
14+
}
15+
return false
16+
}
17+
18+
init(networkMonitor: NWPathMonitor = .init()) {
19+
self.networkMonitor = networkMonitor
20+
startObserving()
21+
}
22+
23+
func startObserving() {
24+
networkMonitor.start(queue: observingQueue)
25+
}
26+
27+
func updateListener(_ listener: @escaping (ConnectivityStatus) -> Void) {
28+
networkMonitor.pathUpdateHandler = { [weak self] path in
29+
guard let self = self else { return }
30+
let connectivityStatus = self.connectivityStatus(from: path)
31+
DispatchQueue.main.async {
32+
listener(connectivityStatus)
33+
}
34+
}
35+
}
36+
37+
func stopObserving() {
38+
networkMonitor.cancel()
39+
}
40+
41+
private func connectivityStatus(from path: NWPath) -> ConnectivityStatus {
42+
let connectivityStatus: ConnectivityStatus
43+
switch path.status {
44+
case .satisfied:
45+
var connectionType: ConnectionType = .other
46+
if path.usesInterfaceType(.wifi) ||
47+
path.usesInterfaceType(.wiredEthernet) {
48+
connectionType = .ethernetOrWiFi
49+
} else if path.usesInterfaceType(.cellular) {
50+
connectionType = .cellular
51+
}
52+
connectivityStatus = .reachable(type: connectionType)
53+
case .unsatisfied:
54+
connectivityStatus = .notReachable
55+
case .requiresConnection:
56+
connectivityStatus = .unknown
57+
@unknown default:
58+
connectivityStatus = .unknown
59+
}
60+
return connectivityStatus
61+
}
62+
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,8 @@
12951295
DE525499268C8B32007A5829 /* UIRefreshControl+Woo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE525498268C8B32007A5829 /* UIRefreshControl+Woo.swift */; };
12961296
DE67D46726B98FD000EFE8DB /* Publisher+WithLatestFrom.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE67D46626B98FD000EFE8DB /* Publisher+WithLatestFrom.swift */; };
12971297
DE67D46926BAA82600EFE8DB /* Publisher+WithLatestFromTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE67D46826BAA82600EFE8DB /* Publisher+WithLatestFromTests.swift */; };
1298+
DE792E1826EF35F40071200C /* ConnectivityObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE792E1726EF35F40071200C /* ConnectivityObserver.swift */; };
1299+
DE792E1B26EF37ED0071200C /* DefaultConnectivityObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE792E1A26EF37ED0071200C /* DefaultConnectivityObserver.swift */; };
12981300
DE7842ED26F061650030C792 /* NumberFormatter+Localized.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE7842EC26F061650030C792 /* NumberFormatter+Localized.swift */; };
12991301
DE7842EF26F079A60030C792 /* NumberFormatter+LocalizedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE7842EE26F079A60030C792 /* NumberFormatter+LocalizedTests.swift */; };
13001302
DE8C94662646990000C94823 /* PluginListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8C94652646990000C94823 /* PluginListViewController.swift */; };
@@ -2730,6 +2732,8 @@
27302732
DE525498268C8B32007A5829 /* UIRefreshControl+Woo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIRefreshControl+Woo.swift"; sourceTree = "<group>"; };
27312733
DE67D46626B98FD000EFE8DB /* Publisher+WithLatestFrom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+WithLatestFrom.swift"; sourceTree = "<group>"; };
27322734
DE67D46826BAA82600EFE8DB /* Publisher+WithLatestFromTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+WithLatestFromTests.swift"; sourceTree = "<group>"; };
2735+
DE792E1726EF35F40071200C /* ConnectivityObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityObserver.swift; sourceTree = "<group>"; };
2736+
DE792E1A26EF37ED0071200C /* DefaultConnectivityObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultConnectivityObserver.swift; sourceTree = "<group>"; };
27332737
DE7842EC26F061650030C792 /* NumberFormatter+Localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+Localized.swift"; sourceTree = "<group>"; };
27342738
DE7842EE26F079A60030C792 /* NumberFormatter+LocalizedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NumberFormatter+LocalizedTests.swift"; sourceTree = "<group>"; };
27352739
DE8C94652646990000C94823 /* PluginListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginListViewController.swift; sourceTree = "<group>"; };
@@ -5003,6 +5007,7 @@
50035007
B55D4C2220B716CE00D7A50F /* Tools */ = {
50045008
isa = PBXGroup;
50055009
children = (
5010+
DE792E1926EF37D80071200C /* Connectivity */,
50065011
CECC759A23D61BCC00486676 /* AggregateData */,
50075012
CEEC9B6121E79EBF0055EEF0 /* AppRatings */,
50085013
CE9B7E3021C94685000F971C /* Currency */,
@@ -6376,6 +6381,15 @@
63766381
path = "Review Order";
63776382
sourceTree = "<group>";
63786383
};
6384+
DE792E1926EF37D80071200C /* Connectivity */ = {
6385+
isa = PBXGroup;
6386+
children = (
6387+
DE792E1726EF35F40071200C /* ConnectivityObserver.swift */,
6388+
DE792E1A26EF37ED0071200C /* DefaultConnectivityObserver.swift */,
6389+
);
6390+
path = Connectivity;
6391+
sourceTree = "<group>";
6392+
};
63796393
DE8C9464264698E800C94823 /* Plugins */ = {
63806394
isa = PBXGroup;
63816395
children = (
@@ -7678,6 +7692,7 @@
76787692
02F67FEE25805F9C00C3BAD2 /* ShippingLabelTrackingURLGenerator.swift in Sources */,
76797693
0269A63C2581D26C007B49ED /* ShippingLabelPrintingStepListView.swift in Sources */,
76807694
3188533C2639FE5800F66A9C /* CardReaderSettingsPresentedViewViewModel.swift in Sources */,
7695+
DE792E1B26EF37ED0071200C /* DefaultConnectivityObserver.swift in Sources */,
76817696
029F29FE24DA5B2D004751CA /* ProductInventorySettingsViewModel.swift in Sources */,
76827697
57CFCD28248845B4003F51EC /* PrimarySectionHeaderView.swift in Sources */,
76837698
023A059A24135F2600E3FC99 /* ReviewsViewController.swift in Sources */,
@@ -7778,6 +7793,7 @@
77787793
571CDD5A250ACC470076B8CC /* UITableViewDiffableDataSource+Helpers.swift in Sources */,
77797794
4580BA7423F192D400B5F764 /* ProductSettingsViewController.swift in Sources */,
77807795
45977EBA2603F632006CDFB8 /* MapsHelper.swift in Sources */,
7796+
DE792E1826EF35F40071200C /* ConnectivityObserver.swift in Sources */,
77817797
45693189265403A1009ED69D /* ShippingLabelCarriersViewModel.swift in Sources */,
77827798
0211254125778BDF0075AD2A /* ShippingLabelDetailsViewController.swift in Sources */,
77837799
020DD49123239DD6005822B1 /* PaginatedListViewControllerStateCoordinator.swift in Sources */,

0 commit comments

Comments
 (0)