Photo by Frederik Lipfert on Unsplash
在iOS12之前想要偵測網路狀態的話,大家都會使用 Reachability 這套官方工具,且有 Demo Code 可以直接使用,所以只要說到偵測網路第一時間一定會想到使用 Reachability。
在 iOS 12之後官方推出了 Network Kit 其中有很多與網路相關的服務,期中一個就可以用來偵測網路狀態,程式碼比過去少又比較清楚,配合現在官方主推的 SwiftUI 也很好用。
Monitor
Create Publisher
像這樣的Monitor 是建議獨立建立一個 Class 來處理,不要放在其他的Class 裡面
import Network
class NetworkMonitor: ObservableObject {
@Published private(set) var isConnected: Bool = false
@Published private(set) var networkType: NWInterface.InterfaceType? = .other
}
首先,記得要 import Network
,為了能讓SwiftUI 使用記得要Conform ObservableObject
isConnected
就是在確認是否為連線狀態,networkType
就是屬於那一種連線類型,官方有分為 wifi
、cellular
…等等,詳細分類可以參考 NWInterface.InterfaceType 的說明。
Create Monitor
let networkMonitor = NWPathMonitor()
let workQueue = DispatchQueue(label: "NetworkMonitor")
接著往下建立 Monitor ,在Network Kit 裡有一個Class 是 NWPathMonitor ,它就是用來偵測網路狀態的,上述這樣的寫法表示要偵測所有型態的網路,有需要的話也可以指定想要偵測的網路型態。
一般來說偵測網路的動作一定不可以影響到主線程的運行,因此我們需要新增一條負責偵測網路用的 DispatchQueue
,名稱可以自訂,這裡是稱它為 NetworkMonitor
。
Create Path update handler
networkMonitor.pathUpdateHandler = { [weak self] path in
self?.isConnected = path.status == .satisfied
self?.networkType = path.availableInterfaces.first?.type
}
當網路狀態改變的時候,可以在 pathUpdateHandler
這個 Closure 取得資訊,會回傳 NWPath
這個物件,它有網路狀態的相關資訊。
Start
networkMonitor.start(queue: workQueue)
最後再開始偵測,記得指定先前設定好的Queue
完整的程式如下
class NetworkMonitor: ObservableObject {
@Published private(set) var isConnected: Bool = false
@Published private(set) var networkType: NWInterface.InterfaceType? = .other
let networkMonitor = NWPathMonitor()
let workQueue = DispatchQueue(label: "NetworkMonitor")
init() {
networkMonitor.pathUpdateHandler = { [weak self] path in
self?.isConnected = path.status == .satisfied
self?.networkType = path.availableInterfaces.first?.type
}
networkMonitor.start(queue: workQueue)
}
}
View
接著就可以將這個NetworkMonitor 放到View 上使用,以下是一個簡單的範例
struct ContentView: View {
@StateObject var networkMonitor = NetworkMonitor()
var body: some View {
VStack {
Label {
Text(networkMonitor.isConnected ? "Connected" : "Disconnected")
} icon: {
Image(systemName: networkMonitor.isConnected ? "network" : "network.slash")
.foregroundStyle(networkMonitor.isConnected ? .green : .red)
}
}
.padding()
}
}
當然,App 應該只會有一個 Network Monitor ,這個時候可以使用 EnvironmentObject
的方式來傳遞 Network Monitor,寫法可以參考以下方式,當然要從哪裡開始建立 Network Monitor 都可以依照個人需求,理論上應該是App 開啟時就需要做偵測啦,記得使用 .environmentObject()
來讓其他畫面可以使用 EnvironmentObject
方式讀取到 Network Monitor。
@main
struct NetworkDemoApp: App {
@StateObject var networkMonitor = NetworkMonitor()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(networkMonitor)
}
}
}
struct ContentView: View {
@EnvironmentObject var networkMonitor: NetworkMonitor
var body: some View {
VStack {
Label {
Text(networkMonitor.isConnected ? "Connected" : "Disconnected")
} icon: {
Image(systemName: networkMonitor.isConnected ? "network" : "network.slash")
.foregroundStyle(networkMonitor.isConnected ? .green : .red)
}
}
.padding()
}
}
最後,祝大家 Coding 愉快!!