我最近寫(xiě)了一篇文章,來(lái)介紹 iOS 在連接新的 Wi-Fi 網(wǎng)絡(luò)時(shí),如何在彈出一個(gè) web view 以讓用戶登錄或注冊(cè)之前,檢測(cè) Captive Portals (強(qiáng)制網(wǎng)絡(luò)門(mén)戶)。如果你連接過(guò)諸如酒店、酒吧或咖啡店等地的公共 Wi-Fi 網(wǎng)絡(luò),對(duì)這個(gè)應(yīng)該會(huì)比較熟悉。如果你不熟悉 iOS 中 Captive Portals 的工作方式,可以查看 Solving the Captive Portal Problem on iOS 這篇文章,以了解一些背景知識(shí)。
專(zhuān)注于為中小企業(yè)提供成都做網(wǎng)站、網(wǎng)站制作服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)二道免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了1000多家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
多年來(lái),Apple 的 Reachability 示例程序一直被用作 App 中檢測(cè)網(wǎng)絡(luò)訪問(wèn)的基礎(chǔ)代碼。搜索 Cocoapods.org 將會(huì)看到一個(gè)很長(zhǎng)的第三方庫(kù)列表,這些庫(kù)基本上都是基于 Reachability,并考慮了 ARC 的支持或 Swift 的兼容等問(wèn)題。
在 WWDC 2018 上,Apple 介紹了 iOS 12 中的一個(gè)新的框架:Network.framework,該框架包含了一個(gè) NWPathMonitor 類(lèi)。這個(gè)類(lèi)為我們提供了一種監(jiān)視網(wǎng)絡(luò)狀態(tài)變化的方法,而無(wú)需包含第三方庫(kù)或 Apple 示例代碼。
使用
只需簡(jiǎn)單導(dǎo)入 Network 框架,便可以使用 NWPathMonitor 類(lèi),如下創(chuàng)建一個(gè) NWPathMonitor 實(shí)例:
let monitor = NWPathMonitor()
如果你只對(duì)某個(gè)特定網(wǎng)絡(luò)適配器的狀態(tài)變更感興趣,例如 Wi-Fi,則可以使用 init(requiredInterfaceType:) 初始化方法,并提供 NWInterface.InterfaceType 值作為參數(shù),來(lái)實(shí)例化 NWPathMonitor 對(duì)象,以監(jiān)聽(tīng)指定類(lèi)型的網(wǎng)絡(luò)適配器,例如:
let monitor = NWPathMonitor(requiredInterfaceType: .wifi)
您需要確保在某處保留對(duì) NWPathMonitor 對(duì)象的引用(例如使用 strong 屬性),否則 ARC 可能會(huì)釋放 NWPathMonitor 對(duì)象,從而導(dǎo)致指定的回調(diào)無(wú)法被調(diào)用。
可監(jiān)控的網(wǎng)絡(luò)類(lèi)型包括:
要獲取狀態(tài)更改的通知,需要為 pathUpdateHandler 屬性指定一個(gè)回調(diào),該回調(diào)將在網(wǎng)絡(luò)接口發(fā)生狀態(tài)更改時(shí)調(diào)用。例如,你的手機(jī)網(wǎng)絡(luò)從蜂窩網(wǎng)絡(luò)切換到 Wi-Fi 網(wǎng)絡(luò)。然后,每當(dāng)發(fā)生狀態(tài)更改時(shí),將返回一個(gè) NWPath 實(shí)例,可以使用該實(shí)例以確定后續(xù)的操作,如下代碼:
monitor.pathUpdateHandler = { path in if path.status == .satisfied { print("Connected") } }
使用無(wú)參初始化方法與使用指定網(wǎng)絡(luò)適配器的初始化方法的不同點(diǎn)是:返回的 NWPathobject 對(duì)象的 status 屬性是否是 satisfied。例如,你只想監(jiān)聽(tīng)蜂窩網(wǎng)絡(luò),而你的手機(jī)連接的是 Wi-Fi 網(wǎng)絡(luò),則當(dāng) Wi-Fi 網(wǎng)絡(luò)狀態(tài)發(fā)生變化時(shí),并不會(huì)調(diào)用回調(diào)方法,并且 path 的 status 也會(huì)保持 unsatisfied 狀態(tài),因?yàn)槭謾C(jī)沒(méi)有使用指定的網(wǎng)絡(luò)連接。所以,如果你只想知道是否有網(wǎng)絡(luò)連接,無(wú)論是 Wi-Fi 還是蜂窩,則最好使用無(wú)參數(shù)的初始化方法。
一個(gè)有趣的問(wèn)題是,NWPath 在 iOS 12 中是作為 Network 框架的一部分,而實(shí)際上在 iOS 9 中就有它的身影,不過(guò)是在 NetworkExtension.framework,兩者之間有一些細(xì)微差別。
可以查詢(xún)返回的 NWPath 對(duì)象,以查看設(shè)備的網(wǎng)絡(luò)適配器的狀態(tài)信息。另一個(gè)更有趣的屬性是 isExpensive,它標(biāo)識(shí)網(wǎng)絡(luò)接口返回的數(shù)據(jù)收費(fèi)是否昂貴,如使用蜂窩數(shù)據(jù)。我們同樣可以查詢(xún)是否支持 DNS、IPv4 或 IPv6。我們可以調(diào)用 usesInterfaceType 方法,來(lái)查看哪個(gè)接口改變了狀態(tài)并觸發(fā)回調(diào):
let isCellular: Bool = path.usesInterfaceType(.cellular)
使用 NWPathMonitor 有點(diǎn)類(lèi)似于使用其他 iOS API,例如 CLLocationManager,我們需要調(diào)用 start 方法以便開(kāi)始接收更新,然后在完成后調(diào)用對(duì)應(yīng)的 stop 方法。NWPathMonitor 的 start 方法要求我們?yōu)閷?duì)象提供一個(gè)隊(duì)列來(lái)執(zhí)行其工作:
let queue = DispatchQueue.global(qos: .background) monitor.start(queue: queue)
當(dāng)我們完成監(jiān)聽(tīng)狀態(tài)的變化時(shí),我們只需在調(diào)用 cancel() 方法。請(qǐng)注意,目前在 NWPathMonitor 上調(diào)用 cancel 后,我們無(wú)法再次啟動(dòng)監(jiān)聽(tīng),而是需要實(shí)例化一個(gè)新的 NWPathMonitor 實(shí)例。
請(qǐng)注意,如果在調(diào)用 start() 之前訪問(wèn) NWPathMonitor 的 currentPath 屬性,將返回 nil。實(shí)際上,如果你打印返回到更新回調(diào)的 path,如下所示:
monitor.pathUpdateHandler = { path in print(path) }
則會(huì)打印以下內(nèi)容:
Optional(satisfied (Path is satisfied), interface: en0, scoped, ipv4, ipv6, dns)
這表明此處返回的 NWPaths 和 currentPath 屬性是可選項(xiàng),盡管 API 沒(méi)有明確說(shuō)明(我們可以推斷返回的 NWPath 引用是橋接到 Swift 的 Objective-C 指針)。
Captive Portals
Captive Portal 是在公共 Wi-Fi 熱點(diǎn)連接時(shí)顯示的網(wǎng)頁(yè),通常用于在授權(quán)訪問(wèn) Internet(或訪問(wèn)其他網(wǎng)絡(luò)資源)之前強(qiáng)制登錄、注冊(cè)或支付。在之前的一篇博客中,我談到了從 App 開(kāi)發(fā)的的角度來(lái)看,Reachability 看起來(lái)好像沒(méi)什么問(wèn)題,但實(shí)際上由于有 Captive Portals,它并不能很好完成任務(wù)。這可能導(dǎo)致 App 無(wú)法正常工作甚至于崩潰 -- 因?yàn)?App 可能期望從 RESTful API 中獲取一些 JSON 數(shù)據(jù),卻從 Captive Portals 獲取到了一些 HTML。
我之前很好奇 NWPathMonitor 在檢測(cè)網(wǎng)絡(luò)連接方面是否比 Reachability 有所改進(jìn)。NWPath.Status 枚舉確實(shí)提供了三種情況 -- satisfied、 unsatisfied 和 requiresConnection。不幸的是,Network.framework 的開(kāi)發(fā)者文檔并未提供這些枚舉值的使用說(shuō)明,而如果我們查看 NetworkExtension.framework 文檔,其中的 NWPathStatus 對(duì)象提供了 satisfiable 枚舉值,里面有一些相關(guān)文檔描述:
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.
requiresConnection 枚舉值似乎類(lèi)似于 NWPathStatus 對(duì)象的 satisfiable 值。
好消息是 NWPathMonitor 通常只在 captive portal 協(xié)商之后通知 path 被設(shè)置為 satisfiable 狀態(tài),即在彈出 web view 且用戶登錄后。而在沒(méi)有彈出 captive portal 的情況下,將向用戶顯示一個(gè) Action Sheet,提供了 Use Without Internet 和 Use Other Network 選項(xiàng)。如果用戶選擇了 Use Without Internet,則 NWPathMonitor 返回的 path 的狀態(tài)是 satisfied,即便實(shí)際上并沒(méi)有連網(wǎng)。
通過(guò)使用 Charles 做的一些實(shí)驗(yàn),我發(fā)現(xiàn)除非選擇 Use Without Internet,否則在初始化 Wi-Fi 網(wǎng)絡(luò)連接的同時(shí)中斷連接的情況下,NWPathMonitor 沒(méi)有報(bào)告 NWPath 的 Status 被置為 statisfied。但是,如果網(wǎng)絡(luò)連接已恢復(fù),但隨后被刪除,則并不能檢測(cè)到這種變更,并且 path 的狀態(tài)未依然是 satisfied。如果用戶僅在火車(chē)或酒店上支付一小時(shí)的互聯(lián)網(wǎng)訪問(wèn)費(fèi)用,這種情況是可能發(fā)生的。
Connectivity
Connectivity 是一個(gè) MIT 許可的開(kāi)源框架,其目的是復(fù)用 iOS 現(xiàn)有的檢測(cè) captive portal 的方法。它允許在 iOS 8+ 上使用 Reachability 準(zhǔn)確檢測(cè)真正的 Internet 連接,這意味著在無(wú)法使用 NWPathMonitor 時(shí),我們可以使用這個(gè)方法。并且在 iOS 12 上,Connectivity 使用了 NWPathMonitor 來(lái)提供更高的準(zhǔn)確度。
Connectivity 已經(jīng)提供了對(duì) NWPathMonitor 的支持,可用于 iOS 12+ 系統(tǒng)。如果 framework 屬性設(shè)置為 network,則會(huì)使用 Network 框架來(lái)替代 SystemConfiguration 框架(Reachability),以監(jiān)聽(tīng)網(wǎng)絡(luò)適配器的狀態(tài)變更。
let connectivity = Connectivity() connectivity.framework = .network
在網(wǎng)絡(luò)適配器中的狀態(tài)更改后,Connectivity 會(huì)執(zhí)行大量檢查以確定 Internet 訪問(wèn)是否可用。另外還有一個(gè)輪詢(xún)選項(xiàng),可以用來(lái)輪詢(xún)網(wǎng)絡(luò)是否可用,即使?fàn)顟B(tài)并未發(fā)生改變。可以通過(guò)設(shè)置 isPollingEnabled = true 并將 pollingInterval 設(shè)置為適當(dāng)?shù)臅r(shí)間值來(lái)實(shí)現(xiàn)這一點(diǎn)。
總結(jié)
Network 框架引入了一些很棒的新類(lèi),包括 NWPathMonitor,可用于在 iOS 12+ 上監(jiān)聽(tīng)設(shè)備網(wǎng)絡(luò)適配器的狀態(tài)變化。在用戶與 captive portal 交互后會(huì)將 path 的狀態(tài)設(shè)置為 satisfied,但不會(huì)檢測(cè)后續(xù)網(wǎng)絡(luò)訪問(wèn)的丟失。Connectivity 可以為支持之前 iOS 系統(tǒng)的 App 提供向后兼容性,并通過(guò)使用 NWPathMonitor 獲取更高的準(zhǔn)確性。
優(yōu)點(diǎn)
缺點(diǎn)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。