Verifique la disponibilidad de conexión a Internet en Swift

107

¿Hay alguna forma de comprobar si la conexión a Internet está disponible con Swift?

Sé que hay muchas bibliotecas de terceros para hacer esto, pero todas están escritas en Objective-C. Estoy buscando una alternativa a Swift.

Isuru
fuente
5
Uno de los grandes beneficios de Swift es que se integra bien con Objective-C (y bibliotecas de este tipo).
user2864740
En efecto. ¿Qué problemas tiene al utilizar una de las bibliotecas existentes? Es bastante simple usar una biblioteca Objective C de Swift, y te resultará extremadamente difícil escribir cualquier tipo de aplicación en Swift si no puedes hacerlo.
Matt Gibson
1
@MattGibson casi todo lo que puede hacer en ObjC se puede hacer en Swift con relativa facilidad. Por supuesto, en este caso habría algunas líneas adicionales, pero aún lejos de ser "extremadamente difícil"
Byron Coetsee
@ByronCoetsee Estaba incluyendo el uso de bibliotecas como AppKit, etc., lo que quiero decir es que necesitará saber cómo interactuar con las bibliotecas Objective C para escribir algo útil en Swift.
Matt Gibson
Ver respuesta de alamofire - stackoverflow.com/a/46562290/7576100
Jack

Respuestas:

228

Como se mencionó en los comentarios, aunque es posible usar bibliotecas Objective-C en Swift, quería una solución Swift más pura. La clase de accesibilidad de Apple existente y otras bibliotecas de terceros me parecieron demasiado complicadas para traducir a Swift. Busqué en Google un poco más y encontré este artículo que muestra un método simple para verificar la disponibilidad de la red. Me propuse traducir esto a Swift. Encontré muchos inconvenientes, pero gracias a Martin R de StackOverflow, logré resolverlos y finalmente obtuve una solución viable en Swift. Aquí está el 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)
    }
}

Esto funciona tanto para conexiones 3G como WiFi. También lo subí a mi GitHub con un ejemplo funcional.

Isuru
fuente
7
Gracias por la rápida respuesta (sin juego de palabras). MIT o Apache serían ideales, ¡gracias!
Andrew Ebling
4
Hecho. Lo cambió a MIT.
Isuru
11
Encontré otro problema, si 3G está encendido pero no tengo más capacidad de datos, entonces isConnectedToNetworkdevuelve verdadero, pero no puedo llamar a mi servicio web
János
11
Como dijo @Isuru, esto se basa en stackoverflow.com/a/25623647/1187415 , que ahora se ha actualizado para Swift 2.
Martin R
2
@ János: ahora es posible configurar una devolución de llamada de notificación con Swift 2, consulte la respuesta actualizada stackoverflow.com/a/27142665/1187415 .
Martin R
16

Te doy mejor camino ...

Debes crear una clase con 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
  }
}

Y luego puede verificar la conexión a Internet en cualquier lugar de su proyecto usando este código:

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

¡Muy fácil!

* ¡De esta manera se basa en la respuesta de Vikram Pote!

Dmitry
fuente
15
Tenga en cuenta que usted debe no utilizar este método sobre el utilizado anteriormente. En aplicaciones que requieren conectividad constante, una verificación de respuesta como este método hará que la aplicación se cuelgue en situaciones en las que su conectividad a Internet sea deficiente (es decir, en Edge / GPRS). ¡Por favor, no use esta solución!
Imran Ahmed
7
Mal camino 1) Necesita un golpe adicional para el servidor cada vez 2) Google.com también puede estar inactivo
Vijay Singh Rana
2
1. Sí, de esta manera se necesita un golpe adicional en el servidor cada vez, pero ofrece una garantía del 100% de que Internet está disponible ... ¡hay ocasiones en las que el dispositivo está conectado a una red wifi pero no proporciona acceso a Internet! En esta situación, esta forma determina la falta de acceso a Internet ... otras formas, ¡no! 2. Puede usar su propio servidor en lugar de google.com ... Esto originalmente significaba ...
Dmitry
1
mala solución, asesino de conexiones.
gokhanakkurt
2
gokhanakkurt, por favor, sugiera otra solución que asegure que Internet funcione al 100%
Dmitry
15

Para Swift 3.1 (iOS 10.1)

Si desea hacer la distinción entre el tipo de red (es decir, WiFi o WWAN):

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

Aquí está toda la clase de accesibilidad que distingue entre tipos de red:

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
fuente
funcionando bien a partir del 3 de diciembre de 2016, iOS 10 y swift 3.1, ¡gracias!
joey
Hola, si queremos diferenciar la conexión Wifi / 3G / Mobile-Data / 4G por eso cómo nos podemos identificar.
6

Dado que sendSynchronousRequest está en desuso, intenté esto pero se llamó a 'return Status' antes de que finalizara la respuesta.

Sin embargo, esta respuesta funciona bien, verifique la conexión a Internet con Swift

Esto es lo que intenté de todos modos:

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
fuente
Me gustó y usé tu solución. Pero agregué un combinado con esta respuesta: stackoverflow.com/a/34591379 aka. agregué un semáforo .. Así que espero a que termine la tarea.
Bjqn
6

SWIFT 3: comprobaciones de la conexión a Internet y wifi :

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
fuente
2
public func isConnectedToNetwork() {...}debe cambiarse a class func isConnectedToNetwork{...}para su caso de uso.
keverly
4

También puede usar la respuesta a continuación.

    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
fuente
¡Gracias! ¿Obtuve una corrección automática de la respuesta como NSHTTPURLResponse? para responder como! NSHTTPURLResponse? en Swift 1.2.
Francis Jervis
1

SWIFT 3: compruebe la conexión 3G y 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
fuente
Esta no es una forma preferida. ¿Qué sucede si en algún momento futuro el enlace web proporcionado deja de responder o no funciona? Recomendaría usar el marco de configuración del sistema de Apple para esto. Vea la respuesta anterior.
Abdul Yasin
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
fuente
1

Rápido 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
fuente