Home 寫程式[iOS] Network Monitor

[iOS] Network Monitor

by 艾普利
Photo by Frederik Lipfert on Unsplash

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 就是屬於那一種連線類型,官方有分為 wificellular…等等,詳細分類可以參考 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 愉快!!

You may also like