By Ross Butler
Medium.com/rwbutler/n…
The public link: mp.weixin.qq.com/s/07oOdkOTP…
I recently wrote about how iOS, when connecting to a new Wi-Fi network, can test Captive Portals before popping up a Web view to allow the user to log in or register. This should be familiar if you’ve ever connected to a public Wi-Fi network in a hotel, bar, or coffee shop. If you’re not familiar with how Captive Portals work in iOS, check out “Solving the Captive Portal Problem on iOS” to get some background.
For years, Apple’s example Reachability has been used as the base code for detecting network access in apps. A search of Cocoapods.org will bring up a long list of third-party libraries that are basically based on Reachability and take into account issues like ARC support or Swift compatibility.
At WWDC 2018, Apple introduced a new framework in iOS 12: Network.framework, which includes an NWPathMonitor class. This class gives us a way to monitor changes in network state without having to include third-party libraries or Apple sample code.
use
Simply import the Network framework and use the NWPathMonitor class to create an instance of NWPathMonitor as follows:
let monitor = NWPathMonitor()
Copy the code
If you are interested in state changes to a particular network adapter, wi-fi, for example, you can use the init (requiredInterfaceType:) initialization method, and provides the NWInterface InterfaceType value as a parameter, To instantiate the NWPathMonitor object to listen for the specified type of network adapter, for example:
let monitor = NWPathMonitor(requiredInterfaceType: .wifi)
Copy the code
You need to make sure that a reference to the NWPathMonitor object is kept somewhere (for example, with the strong attribute), or ARC may release the NWPathMonitor object, causing the specified callback to not be called.
The network types that can be monitored include:
- cellular
- loopback
- Other (for virtual or undefined network types)
- wifi
- wiredEthernet
To get notification of a state change, you need to specify a callback for the pathUpdateHandler property that will be invoked when a state change occurs on the network interface. For example, switch your cell network from cellular to Wi-Fi. Then, whenever a state change occurs, an instance of NWPath is returned, which can be used to determine subsequent actions, as follows:
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
print("Connected")}}Copy the code
The difference between using the no-parameter initialization method and one using a specified network adapter is whether the status property of the returned NWPathobject object is SATISFIED. For example, if you only want to listen on the cellular network, and your phone is connected to a Wi-Fi network, the callback method will not be called when the wi-fi network status changes, and the path status will remain unsatisfied because the phone is not using the specified network connection. So if you just want to know if you have a network connection, whether wi-fi or cellular, it’s best to use the no-argument initialization method.
Is an interesting question, NWPath in iOS 12 as part of the Network framework, but actually has its presence in the iOS 9, but is in NetworkExtension framework, there are some nuances between the two.
You can query the returned NWPath object to view the status information of the network adapter on the device. Another, more interesting attribute is isExpensive, which identifies whether the data returned by the network interface isExpensive, such as using cellular data. We can also query whether DNS, IPv4 or IPv6 are supported. We can call the usesInterfaceType method to see which interface changed state and triggered the callback:
let isCellular: Bool = path.usesInterfaceType(.cellular)
Copy the code
Using NWPathMonitor is somewhat similar to using other iOS apis, such as CLLocationManager, in that we need to call the start method to start receiving updates, and then call the corresponding Stop method when we’re done. The start method of NWPathMonitor requires us to provide an object with a queue to perform its work:
let queue = DispatchQueue.global(qos: .background)
monitor.start(queue: queue)
Copy the code
When we are done listening for the state change, we simply call the cancel() method again. Note that now after calling Cancel on NWPathMonitor, we cannot start listening again and need to instantiate a new instance of NWPathMonitor.
Note that if the currentPath property of NWPathMonitor is accessed before start() is called, nil is returned. In fact, if you print path back to the update callback, it looks like this:
monitor.pathUpdateHandler = { path in
print(path)
}
Copy the code
The following is printed:
Optional(satisfied (Path is satisfied), interface: en0, scoped, ipv4, ipv6, dns)
Copy the code
This indicates that the NWPaths and currentPath properties returned here are optional, although not explicitly stated by the API (we can infer that the returned NWPath reference is an Objective-C pointer that Bridges to Swift).
Captive Portals
Captive Portal is a web page displayed when connecting to a public Wi-Fi hotspot and is typically used to force login, registration, or payment before authorized access to the Internet (or other network resources). In a previous blog post, I talked about how Reachability seems to be fine from the perspective of App development, but actually it doesn’t do a very good job because of Captive Portals. This can cause the App to not work properly or even crash — because the App might expect some JSON data from the RESTful API and get some HTML from Captive Portals.
I was wondering if NWPathMonitor has improved on Reachability in detecting network connections. The nwPath. Status enumeration does provide three cases — satisfied, unsatisfied, and requiresConnection. . Unfortunately, the Network framework of the developer documentation does not provide the use of these enumerated values, and if we see NetworkExtension. The framework document, Satisfiable: NWPathStatus (); satisfiable: satisfiable;
The path is not currently satisfied, but may become satisfied upon a connection attempt. This can be due to a service, such as a VPN or a cellular data connection not being activated.
The requiresConnection enumeration value seems to be similar to the Satisfiable value of the NWPathStatus object.
The good news is that NWPathMonitor usually notifies the path of being set to satisfiable only after the captive Portal negotiation, which is after the Web view is displayed and the user logs in. If no captive portal is displayed, the user is presented with an Action Sheet that provides the Use Without Internet and Use Other Network options. If the user selects Use Without Internet, the path state returned by NWPathMonitor is SATISFIED, even if the user is not actually connected to the Internet.
Through some experiments with Charles, I found that unless Use Without Internet is selected, when the Wi-Fi network connection is initialized and disconnected, NWPathMonitor does not report that the Status of NWPath has been set to statisfied. However, if the network connection has been restored but subsequently deleted, the change is not detected and path is not still in the satisfied state. This can happen if users only pay for an hour of Internet access on a train or hotel.
Connectivity
Connectivity is an MIT-licensed open source framework designed to reuse iOS’s existing method of detecting captive Portal. It allows for accurate detection of real Internet connections using Reachability on iOS 8+, which means we can use this method when NWPathMonitor is not available. And on iOS 12, Connectivity uses NWPathMonitor to provide even greater accuracy.
Connectivity already provides support for NWPathMonitor for iOS 12+. If the framework property is set to Network, the Network framework replaces the SystemConfiguration bility (Reachability) to listen for network adapter status changes.
let connectivity = Connectivity()
connectivity.framework = .network
Copy the code
After a state change in the network adapter, Connectivity performs extensive checks to determine whether Internet access is available. There is also a polling option for polling whether the network is available, even if the state has not changed. You can do this by setting isPollingEnabled = true and setting the pollingInterval to the appropriate time value.
conclusion
The Network framework introduces some great new classes, including NWPathMonitor, which can be used to listen for changes in the state of a device’s Network adapter on iOS 12+. The path state is set to SATISFIED after the user interacts with captive Portal, but subsequent loss of network access is not detected. Connectivity provides backward compatibility for apps that support previous iOS systems and provides greater accuracy by using NWPathMonitor.
advantages
- Apple official support;
- No need to include third-party code – just import Network. Framework in iOS 12;
- Report NWPath’s status as satisfied after negotiation with captive Portal;
disadvantages
- It can’t be used before iOS 12, which means that if you need to support earlier versions of iOS, it’s a bit of a hassle;
- Lack of detailed documentation;
- Captive Portals and other Internet connection interrupts are not detected after the initial connection is successful.
A link to the
- Solving the Captive Portal Problem on iOS
- Connectivity
Pay attention to our
Please follow our official account ios-tips and join our group to discuss issues. You can leave messages on the public account ios, flutter and other keywords to get into the group.