检查网络连接(iOS 10)

53

在iOS 9中,我使用了可达性公共类来检查设备是否连接到互联网。我将我的Swift 2代码转换为Swift 3后,Reachability不再起作用了。有人能告诉我如何在 iOS 10 上检查互联网连接吗?谢谢!以下是代码片段:

open class Reachability {
    class func isConnectedToNetwork() -> 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) {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }
        var flags = SCNetworkReachabilityFlags()
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
            return false
        }
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        return (isReachable && !needsConnection)
    }
}

1
Swift 3中UnsafePointer(0)不再在Xcode 8 beta 6中编译 - MartinMcB
2
请注意,https://dev59.com/Xl8e5IYBdhLWcg3wja_5#25623647 已更新为Swift 3。 - Martin R
我认为这会对你的问题有所帮助: http://stackoverflow.com/a/39783037/4301118 - sharon
请查看alamofire的答案 - https://dev59.com/gF0a5IYBdhLWcg3wD1Lb#46562290 - Jack
12个回答

112
import Foundation
import SystemConfiguration

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)
        return (isReachable && !needsConnection)
    }

这适用于iOS 10


2
这个答案不错,但还不够。如果你要使用这个函数,你必须导入SystemConfiguration。 - Phd. Burak Öztürk
7
如果您已连接到 Wi-Fi 但无法连接到互联网(打开 Google 页面),仍然返回可达的真值,这是错误的。 - Arildo Junior
4
当连接到没有互联网的WiFi时,这会返回“true”,这对我的情况是个问题。 - Pavle Mijatovic
1
@PavleMijatovic 那么这个问题的解决方案是什么(在连接到没有互联网的WiFi时返回“true”)?如果您找到了解决方案,请也请告诉我。 - Tejas
@PavleMijatovic:好的,谢谢! :) - Tejas
显示剩余2条评论

63

SWIFT 3.0: 这里有一个非常简单的方法:

import SystemConfiguration


protocol Utilities {
}

extension NSObject:Utilities{


    enum ReachabilityStatus {
        case notReachable
        case reachableViaWWAN
        case reachableViaWiFi
    }

    var currentReachabilityStatus: ReachabilityStatus {

        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 .notReachable
        }

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

        if flags.contains(.reachable) == false {
            // The target host is not reachable.
            return .notReachable
        }
        else if flags.contains(.isWWAN) == true {
            // WWAN connections are OK if the calling application is using the CFNetwork APIs.
            return .reachableViaWWAN
        }
        else if flags.contains(.connectionRequired) == false {
            // If the target host is reachable and no connection is required then we'll assume that you're on Wi-Fi...
            return .reachableViaWiFi
        }
        else if (flags.contains(.connectionOnDemand) == true || flags.contains(.connectionOnTraffic) == true) && flags.contains(.interventionRequired) == false {
            // The connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs and no [user] intervention is needed
            return .reachableViaWiFi
        } 
        else {
            return .notReachable
        }
    }

}

然后你可以在项目的任何地方使用它,例如:

  func viewDidLoad(){
      print(currentReachabilityStatus != .notReachable) //true connected
    }

3
这是最好的解决方案,而且非常迅速。 - Kegham K.
3
如果您已连接到 Wi-Fi 但无法连接到互联网(打开 Google 页面),仍然返回可达的真值,这是错误的。 - Arildo Junior
1
我希望我能给这个答案更多的赞!它应该被接受为解决方案。 - Scooter
这个在没有互联网连接的情况下返回"true",对我的情况来说是个问题。 - Codetard
当WiFi连接但无法访问互联网时,此代码无法正常工作。 - Tina
显示剩余6条评论

11

你可以试一下这个方法,对我很有效。首先在你的类中导入SystemConfiguration

import SystemConfiguration

现在实现下面的函数。

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(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.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
    let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0

    return (isReachable && !needsConnection)

}

看起来这段代码存在缺陷。使用 == 检查标志位不能正常工作。 - ryantxr
亲爱的 @ryantxr,这段代码对我来说运行正常,希望你正在使用Swift 3.0,如果你找到更好的解决方案,可以在这里分享,但请不要将答案评为负分。 - Patel Jigar
1
你可以使用 flags.contains(.reachable)flags.contains(.connectionRequired) 来修复它。 是的,我正在使用 Swift 3。 - ryantxr
3
如果你已连接到 Wi-Fi 但无法连接到互联网(打开 Google 页面)仍然会返回可达的结果,那是错误的。请注意修改。 - Arildo Junior
这段代码给我带来了问题。它在WiFi下可以正常工作,但如果我使用蜂窝数据连接并检查,它会显示没有网络,即使网络是可用的。 - miOS
@ArildoJunior 进行响应数据检查。例如,如果响应数据为nil,则表示您处于离线状态。 - Codetard

8

步骤1:在您的项目中创建一个Swift文件。我创建了“ConnectionCheck.swift”。

步骤2:将以下代码添加到您的“ConnectionCheck.swift”文件,并将“SystemConfiguration”文件导入您的“ConnectionCheck.swift”和“ViewController.swift”中。

import Foundation
import SystemConfiguration
public class ConnectionCheck {

class 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)
}

步骤3:现在在“ViewController.swift”中使用以下代码来检查网络可达性。
if ConnectionCheck.isConnectedToNetwork() {
        print("Connected")
    }
    else{
        print("disConnected")
    }

如果我向苹果提交我的应用程序,是否需要更改任何代码以通过苹果验证? - Eric Chong
1
@LOLXDXPLOL 是的,我们可以看到... 但是SO与多个答案一起工作,因此其他人可以看到不同的版本,也许会出现更好的解决方案... 我试图保持礼貌。 - Byron Coetsee
@ByronCoetsee 抱歉,我不想听起来不礼貌,我只是没有那么想过。谢谢! - LOLXDXPLOL

4
这段代码对你很有帮助,它在Swift 4和Xcode 9中都可用。
请前往https://github.com/ashleymills/Reachability.swift/tree/master/Reachability,并将 Reachability.swift 文件复制到你的项目中。
Vasil Nunev非常清晰地解释了这个 Reachability,你可以观看https://www.youtube.com/watch?v=wDZmz9IsB-8
class ViewController: UIViewController {

   let reachability = Reachability();
    override func viewDidLoad() {
        super.viewDidLoad()

        reachability?.whenReachable = { _ in

            DispatchQueue.main.async {
                self.view.backgroundColor = UIColor.green
            }
        }

        reachability?.whenUnreachable = { _ in
            DispatchQueue.main.async {
                self.view.backgroundColor = UIColor.red
            }
        }
        NotificationCenter.default.addObserver(self, selector: #selector(internetChanged), name: Notification.Name.reachabilityChanged , object: reachability)
        do{

            try reachability?.startNotifier()
        }catch{
            print("Could not start notifier:\(error)")
        }

    }

    @objc func internetChanged(note:Notification)  {
        let reachability  = note.object as! Reachability

        if reachability.connection != .none {

            if reachability.connection == .wifi{
                DispatchQueue.main.async {
                    self.view.backgroundColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
                }
            }else if reachability.connection == .cellular{
                DispatchQueue.main.async {
                    self.view.backgroundColor = #colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1)
                }
            }
        }else{
            DispatchQueue.main.async {
                self.view.backgroundColor = #colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1)
            }
        }
    }
}

谢谢,但我很久以前就已经得到了一个可行的答案。 - LOLXDXPLOL
这个方法对蜂窝数据和WiFi都适用吗?它能区分仅连接到WiFi和连接到带有互联网连接的WiFi吗? - Runeaway3
这段代码对我来说不起作用。我不知道我做错了什么。当你进入网络时,它会返回true然后false。 - iOS_Developer

3

以下是使用回调函数的Swift 3解决方案,isConnectedToNetwork()采用了上面由Yasin Ugurlu提供的解决方案。

class func isInternetAvailable(webSiteToPing: String?, completionHandler: @escaping (Bool) -> Void) {

    // 1. Check the WiFi Connection
    guard isConnectedToNetwork() else {
      completionHandler(false)
      return
    }

    // 2. Check the Internet Connection
    var webAddress = "https://www.google.com" // Default Web Site
    if let _ = webSiteToPing {
      webAddress = webSiteToPing!
    }

    guard let url = URL(string: webAddress) else {
      completionHandler(false)
      print("could not create url from: \(webAddress)")
      return
    }

    let urlRequest = URLRequest(url: url)
    let session = URLSession.shared
    let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
      if error != nil || response == nil {
        completionHandler(false)
      } else {
        completionHandler(true)
      }
    })

    task.resume()
  }

你的答案看起来不错,你上面关于已接受答案连接到WiFi但没有互联网的评论是正确的。你如何调用函数并在视图控制器中使用它?请给出一个例子。 - Lance Samaria

1

Swift 4 / Xcode 10:

我将结合@Pavle Mijatovic和@Krutagn Patel的解决方案(感谢他们)并进行一些更改,以回答@Lance Samaria的问题。 :)

我几乎检查了所有可能性。(代码可以优化,但它运行得很好)

有一个如下的类:

import Foundation
import SystemConfiguration

public class ConnectionCheck {

    class 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)
    }


    class func isInternetAvailable(webSiteToPing: String?, completionHandler: @escaping (_ b: Bool) -> Void) {

        // 1. Check the WiFi Connection
        guard isConnectedToNetwork() else {
            completionHandler(false)
            return
        }

        // 2. Check the Internet Connection
        var webAddress = "https://www.google.com" // Default Web Site
        if let _ = webSiteToPing {
            webAddress = webSiteToPing!
        }

        guard let url = URL(string: webAddress) else {
            completionHandler(false)
            print("could not create url from: \(webAddress)")
            return
        }

        let urlRequest = URLRequest(url: url)
        let session = URLSession.shared
        let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
            if error != nil || response == nil {
                completionHandler(false)
            } else {
                completionHandler(true)
            }
        })

        task.resume()
    }
}

在需要检查网络连接的地方使用下面的代码:例如,在appDelegate -> applicationDidBecomeActive函数中。

解释:每当调用completionHandler函数(如上所示),结果(true或false)将作为b传递到下面的代码中。因此,如果b为true,则表示您有Internet连接。

let web : String? = "https://www.google.com"
ConnectionCheck.isInternetAvailable(webSiteToPing: web) { (b)  in
 print(b)
if b {
print("connected") // or do something here
    } else {
print("disconnected") // or do something here
}
}

0

Swift 4/Xcode 9+(创建Reachability.swift文件):

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(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 = SCNetworkReachabilityFlags(rawValue: 0)
        if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
            return false
        }

        // Working for Cellular and WIFI
        let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
        let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
        let ret = (isReachable && !needsConnection)

        return ret

    }
}

用法:

if Reachability.isConnectedToNetwork() {
// connected, do something
}

0
  1. 在您的项目中创建一个Swift文件,命名为:Reachability.swift",然后将以下代码粘贴到这个Reachability.swift文件中:

    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(kCFAllocatorDefault, UnsafePointer($0))
            }
    
            var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
            if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
                return false
            }
    
            let isReachable = flags == .Reachable
            let needsConnection = flags == .ConnectionRequired
    
            return isReachable && !needsConnection
    
        }
    }
    

然后您可以使用以下代码在项目中的任何地方检查互联网

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

0

1)在您的项目中创建一个新的Swift文件,将其命名为“Reachability.swift”。

2)将以下代码剪切并粘贴到其中以创建您的类。

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(kCFAllocatorDefault, UnsafePointer($0))
    }

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

    let isReachable = flags == .Reachable
    let needsConnection = flags == .ConnectionRequired

    return isReachable && !needsConnection

     }
}

3) 你可以使用以下代码在项目的任何地方检查互联网连接:

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

4) 如果用户没有连接到互联网,您可能希望显示一个警告对话框来通知他们。

if Reachability.isConnectedToNetwork() == true {
println("Internet connection OK")
} else {
println("Internet connection FAILED")
var alert = UIAlertView(title: "No Internet Connection", message: "Make sure your 
 device is connected to the internet.", delegate: nil, cancelButtonTitle: "OK")
alert.show()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接