Verifique a disponibilidade de conexão à Internet em Swift

107

Existe uma maneira de verificar se a conexão com a Internet está disponível usando Swift?

Eu sei que existem muitas bibliotecas de terceiros para fazer isso, mas todas são escritas em Objective-C. Estou procurando uma alternativa Swift.

Isuru
fonte
5
Um dos grandes benefícios do Swift é que ele se integra bem com Objective-C (e bibliotecas deste).
user2864740
De fato. Que problemas você está tendo ao usar uma das bibliotecas existentes? É muito simples usar uma biblioteca Objective C do Swift e será extremamente difícil para você escrever qualquer tipo de aplicativo em Swift se você não puder fazê-lo.
Matt Gibson de
1
@MattGibson quase tudo que você pode fazer no ObjC pode ser feito no Swift com relativa facilidade. Concedido, neste caso haveria algumas linhas extras, mas ainda longe de ser "extremamente difícil"
Byron Coetsee
@ByronCoetsee Eu estava incluindo o uso de bibliotecas como AppKit, etc. - meu ponto é que você precisa saber como interagir com bibliotecas Objective C para escrever algo útil em Swift.
Matt Gibson
Veja a resposta de alamofire - stackoverflow.com/a/46562290/7576100
Jack

Respostas:

227

Conforme mencionado nos comentários, embora seja possível usar bibliotecas Objective-C em Swift, eu queria uma solução Swift mais pura. A classe existente de Reachability da Apple e outras bibliotecas de terceiros pareciam ser muito complicadas para eu traduzir para o Swift. Pesquisei mais no Google e encontrei este artigo que mostra um método simples para verificar a disponibilidade da rede. Decidi traduzir isso para o Swift. Tive muitos problemas, mas graças a Martin R do StackOverflow, consegui resolvê-los e, finalmente, obter uma solução viável em Swift. Aqui está o código.

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
    }

}

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

Isso funciona para conexões 3G e WiFi. Eu também fiz o upload para meu GitHub com um exemplo funcional.

Isuru
fonte
7
Obrigado pela resposta rápida (sem trocadilhos). MIT ou Apache seriam ideais - obrigado!
Andrew Ebling
4
Feito. Mudou para MIT.
Isuru
11
Encontrei outro problema, se o 3G estiver ligado, mas não tenho mais capacidade de dados, então isConnectedToNetworkretorna verdadeiro, mas não consigo ligar para o meu serviço web
János
11
Como @Isuru disse, isso é baseado em stackoverflow.com/a/25623647/1187415 , que agora foi atualizado para o Swift 2.
Martin R
2
@ János: Agora é possível definir um retorno de chamada de notificação com o Swift 2, consulte a resposta atualizada stackoverflow.com/a/27142665/1187415 .
Martin R de
16

Eu te dou uma maneira melhor ...

Você deve criar uma classe com este código

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

E então você pode verificar a conexão com a Internet em qualquer lugar em seu projeto usando este código:

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

Muito fácil!

* Esta forma é baseada na resposta de Vikram Pote!

Dmitry
fonte
15
Esteja ciente de que você não deve usar este método sobre o usado acima. Em aplicativos que exigem conectividade constante, uma verificação de resposta como este método resultará no travamento do aplicativo em situações em que a conectividade com a Internet é ruim (ou seja, no Edge / GPRS). Por favor, não use esta solução !!
Imran Ahmed
7
Maneira incorreta 1) Precisa de hit extra para o servidor cada vez 2) google.com também pode estar fora do ar
Vijay Singh Rana
2
1. Sim, desta forma precisa de acesso extra ao servidor todas as vezes, mas dá 100% de garantia de que a Internet está disponível ... há momentos em que o dispositivo está conectado a uma rede wi-fi mas não fornece acesso à Internet! Nessa situação, essa forma determina a falta de acesso à internet ... outras formas - não! 2. Você pode usar seu próprio servidor em vez do google.com ... Isso foi feito originalmente ...
Dmitry
1
solução ruim, assassino de conexão.
gokhanakkurt
2
gokhanakkurt, por favor, sugira outra solução que garanta que a Internet funcione 100%
Dmitry
15

Para Swift 3.1 (iOS 10.1)

Se você quiser fazer a distinção entre o tipo de rede (ou seja, WiFi ou WWAN):

Você pode usar:

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

Aqui está toda a classe de acessibilidade que diferencia os tipos de rede:

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
        }
    }
}
iKK
fonte
funcionando bem desde 3 de dezembro de 2016, iOS 10 e swift 3.1, obrigado!
joey
Olá, se queremos diferenciar a conexão Wifi / 3G / Mobile-Data / 4G para isso como podemos identificar.
6

Como sendSynchronousRequest está obsoleto, tentei fazer isso, mas 'return Status' foi chamado antes de a resposta terminar.

Porém, esta resposta funciona bem, verifique a conexão com a Internet com o Swift

Aqui está o que eu tentei de qualquer maneira:

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
    }
}
Sarah
fonte
Gostei e usei a sua solução. Mas eu adicionei um combinado com esta resposta: stackoverflow.com/a/34591379 aka. Eu adicionei um semáforo .. Então, espero que a tarefa termine.
Bjqn
6

SWIFT 3: verifica a existência de wi - fi e conexão à Internet :

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

USO:

if Reachability.isConnectedToNetwork() == true {
    print("Connected to the internet")
    //  Do something
} else {
    print("No internet connection")
    //  Do something
}
Gilad Brunfman
fonte
2
public func isConnectedToNetwork() {...}deve ser alterado para class func isConnectedToNetwork{...}para o seu caso de uso.
keverly
4

Você também pode usar a resposta abaixo.

    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
        }
    })
   }
Vikram Pote
fonte
Obrigado! Recebi uma correção automática da resposta como NSHTTPURLResponse? para responder como! NSHTTPURLResponse? em Swift 1.2.
Francis Jervis
1

SWIFT 3: Verifique a conexão 3G e Wi-Fi

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()
}
Włodzimierz Woźniak
fonte
Esta não é a forma preferida. E se em algum momento no futuro o link da web fornecido parar de responder ou ficar inativo. Eu recomendaria usar o framework Apple SystemConfiguration para isso. Veja a resposta acima.
Abdul Yasin de
1

Para 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
}
RandallShanePhD
fonte
1

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)
}
Keshav Gera
fonte