检查 Swift 中的互联网连接可用性

有没有办法使用 Swift 检查互联网连接是否可用?

我知道有许多第三方库可以做到这一点,但它们都是用 Objective-C 编写的。我在找斯威夫特的替代品。

92491 次浏览

As mentioned in the comments, although its possible to use Objective-C libraries in Swift, I wanted a more pure Swift solution. The existing Apple Reachability class and other third party libraries seemed to be too complicated for me to translate to Swift. I Googled some more and I came across this article which shows a simple method to check for network availability. I set out to translate this to Swift. I hit many snags but thanks to Martin R from StackOverflow, I managed to resolve them and finally get a workable solution in Swift. Here is the code.

import Foundation
import SystemConfiguration


public class Reachability {


class func isConnectedToNetwork() -> Bool {


var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)


let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)).takeRetainedValue()
}


var flags: SCNetworkReachabilityFlags = 0
if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 {
return false
}


let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0


return isReachable && !needsConnection
}


}

For Swift > 3.0

public class Reachability {
public func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)


guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}) else {
return false
}


var flags: SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}
if flags.isEmpty {
return false
}


let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)


return (isReachable && !needsConnection)
}
}

This works for both 3G and WiFi connections. I've also uploaded it to my GitHub with a working example.

You can also use below answer.

    func checkInternet(flag:Bool, completionHandler:(internet:Bool) -> Void)
{
UIApplication.sharedApplication().networkActivityIndicatorVisible = true


let url = NSURL(string: "http://www.google.com/")
let request = NSMutableURLRequest(URL: url!)


request.HTTPMethod = "HEAD"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
request.timeoutInterval = 10.0


NSURLConnection.sendAsynchronousRequest(request, queue:NSOperationQueue.mainQueue(), completionHandler:
{(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in


UIApplication.sharedApplication().networkActivityIndicatorVisible = false


let rsp = response as NSHTTPURLResponse?


completionHandler(internet:rsp?.statusCode == 200)
})
}


func yourMethod()
{
self.checkInternet(false, completionHandler:
{(internet:Bool) -> Void in


if (internet)
{
// "Internet" mean Google
}
else
{
// No "Internet" no Google
}
})
}

I give you better way...

You must create a class with this code

 import Foundation
public class Reachability {


class func isConnectedToNetwork()->Bool{


var Status:Bool = false
let url = NSURL(string: "http://google.com/")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "HEAD"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
request.timeoutInterval = 10.0


var response: NSURLResponse?


var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil) as NSData?


if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode == 200 {
Status = true
}
}


return Status
}
}

And then you can check internet connection anywhere in your project using this code:

if Reachability.isConnectedToNetwork() == true {
println("Internet connection OK")
} else {
println("Internet connection FAILED")
}

Very easy!

*This way is based on Vikram Pote answer!

Since sendSynchronousRequest is deprecated, I tried this but 'return Status' was called before the response had finished.

This answer works well though, Check for internet connection with Swift

Here's what I tried anyway:

import Foundation


public class Reachability {


class func isConnectedToNetwork()->Bool{


var Status:Bool = false
let url = NSURL(string: "http://google.com/")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "HEAD"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
request.timeoutInterval = 10.0
let session = NSURLSession.sharedSession()


session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
print("data \(data)")
print("response \(response)")
print("error \(error)")


if let httpResponse = response as? NSHTTPURLResponse {
print("httpResponse.statusCode \(httpResponse.statusCode)")
if httpResponse.statusCode == 200 {
Status = true
}
}


}).resume()




return Status
}
}

For Swift 3.1 (iOS 10.1)

If you want to make the distinction between the network-type (i.e. WiFi or WWAN):

You can use:

func checkWiFi() -> Bool {


let networkStatus = Reachability().connectionStatus()
switch networkStatus {
case .Unknown, .Offline:
return false
case .Online(.WWAN):
print("Connected via WWAN")
return true
case .Online(.WiFi):
print("Connected via WiFi")
return true
}
}

Here is the entire Reachability-Class that distinguished between network-types:

import Foundation
import SystemConfiguration


import UIKit
import SystemConfiguration.CaptiveNetwork


public let ReachabilityStatusChangedNotification = "ReachabilityStatusChangedNotification"


public enum ReachabilityType: CustomStringConvertible {
case WWAN
case WiFi


public var description: String {
switch self {
case .WWAN: return "WWAN"
case .WiFi: return "WiFi"
}
}
}


public enum ReachabilityStatus: CustomStringConvertible  {
case Offline
case Online(ReachabilityType)
case Unknown


public var description: String {
switch self {
case .Offline: return "Offline"
case .Online(let type): return "Online (\(type))"
case .Unknown: return "Unknown"
}
}
}


public class Reachability {


func connectionStatus() -> ReachabilityStatus {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)


guard let defaultRouteReachability = (withUnsafePointer(to: &zeroAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) { zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}) else {
return .Unknown
}


var flags : SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return .Unknown
}


return ReachabilityStatus(reachabilityFlags: flags)
}


func monitorReachabilityChanges() {
let host = "google.com"
var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
let reachability = SCNetworkReachabilityCreateWithName(nil, host)!


SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
let status = ReachabilityStatus(reachabilityFlags: flags)


NotificationCenter.default.post(name: NSNotification.Name(rawValue: ReachabilityStatusChangedNotification), object: nil, userInfo: ["Status": status.description])}, &context)


SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue)
}
}


extension ReachabilityStatus {


public init(reachabilityFlags flags: SCNetworkReachabilityFlags) {
let connectionRequired = flags.contains(.connectionRequired)
let isReachable = flags.contains(.reachable)
let isWWAN = flags.contains(.isWWAN)


if !connectionRequired && isReachable {
if isWWAN {
self = .Online(.WWAN)
} else {
self = .Online(.WiFi)
}
} else {
self =  .Offline
}
}
}

SWIFT 3: Check 3G & Wi-Fi connection

DispatchQueue.main.async {
let url = URL(string: "https://www.google.com")!
let request = URLRequest(url: url)


let task = URLSession.shared.dataTask(with: request) {data, response, error in


if error != nil {
// do something here...
print("Internet Connection not Available!")
}
else if let httpResponse = response as? HTTPURLResponse {
if httpResponse.statusCode == 200 {
// do something here...
print("Internet Connection OK")
}
print("statusCode: \(httpResponse.statusCode)")
}


}
task.resume()
}

Checks for wifi and internet connection:

import Foundation
import SystemConfiguration


public class Reachability {
public func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)


guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}) else {
return false
}


var flags: SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}


let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)


return (isReachable && !needsConnection)
}
}

USAGE:

if Reachability.isConnectedToNetwork() == true {
print("Connected to the internet")
//  Do something
} else {
print("No internet connection")
//  Do something
}

Swift 4

if isInternetAvailable() {
print("if called Internet Connectivity success \(isInternetAvailable())");
} else {
print("else called Internet Connectivity success \(isInternetAvailable())");
}


func isInternetAvailable() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}


var flags = SCNetworkReachabilityFlags()


if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
return false
}
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
//   print(isReachable && !needsConnection)
return (isReachable && !needsConnection)
}

For Swift 5:

import Network
let monitor = NWPathMonitor()


func checkInterwebs() -> Bool {
var status = false
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
status = true  // online
}
}
return status
}

If you use Alamofire in your project you can Use this sample func:

import Alamofire


class Network {


func isConnected(_ complition: @escaping(Bool) -> Void) {
let retVal = NetworkReachabilityManager()!.isReachable
complition(retVal)
}


}

If you just want to know if it's connected or not and you're using SwiftUI:

Service:

import Foundation
import Network


class ConnectionService: ObservableObject {
@Published var connected: Bool = false
    

private var monitor: NWPathMonitor
    

init() {
monitor = NWPathMonitor()


monitor.pathUpdateHandler = { [weak self] path in
switch path.status {
case .satisfied:
self?.connected = true
case .unsatisfied, .requiresConnection:
self?.connected = false
@unknown default:
self?.connected = false
}
}


monitor.start(queue: DispatchQueue.main)
}
}

Usage:

// step 1: add in your App struct
@StateObject var internet = ConnectionService()


// step 2: add this modifier to your top level view
.environmentObject(internet)


// step 3: add this in any child view
@EnvironmentObject var internet: ConnectionService


// step 4: example usage in that child view
Text("internet: \(internet.connected ? "true" : "false")")