¿La forma más fácil de detectar la conexión a Internet en iOS?


147

Sé que esta pregunta parecerá ser un engaño de muchos otros, sin embargo, no creo que el caso simple esté bien explicado aquí. Procedente de un fondo de Android y BlackBerry, la realización de solicitudes HTTPUrlConnectionfalla instantáneamente si no hay conexión disponible. Esto parece un comportamiento completamente sano, y me sorprendió descubrir que NSURLConnectionen iOS no lo emulaba.

Entiendo que Apple (y otros que lo han extendido) brindan una Reachabilityclase para ayudar a determinar el estado de la red. Estaba feliz de ver esto por primera vez y esperaba ver algo así bool isNetworkAvailable(), pero para mi sorpresa, encontré un sistema complejo que requería registros de notificación y devoluciones de llamadas, y un montón de detalles aparentemente innecesarios. Debe haber una mejor manera.

Mi aplicación ya maneja correctamente las fallas de conexión, incluida la falta de conectividad. El usuario recibe una notificación de la falla y la aplicación continúa.

Por lo tanto, mis requisitos son simples: función síncrona única que puedo llamar antes de todas las solicitudes HTTP para determinar si debo molestarme en enviar la solicitud o no. Idealmente, no requiere configuración y solo devuelve un valor booleano.

¿Realmente esto no es posible en iOS?



Nunca debe llamar a un método de alcance de red síncrono antes de cada solicitud HTTP; es un poco loco hacer ese trabajo a menos que la accesibilidad le diga asincrónicamente que hubo un cambio en las condiciones de la red y luego puede elegir no enviar la solicitud HTTP. Esta es también la razón por la que existen los tiempos de espera (y luego debe manejar ese escenario con gracia). Vea el enlace de arriba para ver la forma asíncrona
robaron

Respuestas:


251

Investigué un poco más y estoy actualizando mi respuesta con una solución más actual. No estoy seguro si ya lo ha mirado, pero hay un buen código de muestra proporcionado por Apple.

Descargue el código de muestra aquí

Incluya los archivos Reachability.h y Reachability.m en su proyecto. Eche un vistazo a ReachabilityAppDelegate.m para ver un ejemplo sobre cómo determinar la accesibilidad del host, la accesibilidad por WiFi, WWAN, etc. Para una simple verificación de la accesibilidad de la red, puede hacer algo como esto

Reachability *networkReachability = [Reachability reachabilityForInternetConnection];   
NetworkStatus networkStatus = [networkReachability currentReachabilityStatus];    
if (networkStatus == NotReachable) {        
    NSLog(@"There IS NO internet connection");        
} else {        
     NSLog(@"There IS internet connection");        
}

@ BenjaminPiette's: No olvides agregar SystemConfiguration.framework a tu proyecto.


46
No olvide agregar SystemConfiguration.framework a su proyecto.
Benjamin Piette

1
@chandru Funciona. Esta es la forma más fácil y efectiva que veo para detectar la conexión de red. ¡Simple y fácil!
S1U

3
Cuidado con esto. Estoy descubriendo que no funciona de manera confiable cuando se restaura la red, al menos en el simulador. Me inicio con wifi apagado, y correctamente informa que no hay red disponible; pero luego vuelvo a encender el wifi y SCNetworkReachabilityGetFlags continúa devolviendo 0, aunque las consultas de red reales funcionan bien.
Joe Strout

2
Creo que mi respuesta a continuación ( stackoverflow.com/a/26593728/557362 ) requiere menos trabajo (sin descargar otros archivos), pero esa es solo mi opinión.
GilesDMiddleton

3
Tenga en cuenta que esto solo le indica si la conexión al host específico es enrutable . Eso no significa que realmente puedas conectarte. Ejemplo: conecta el wifi de su iPhone al wifi de su cámara. Wifi está conectado con una puerta de enlace predeterminada: todos los hosts se comunicarán como accesibles. Pero en realidad no lo son: la cámara es una conexión sin salida.
xaphod

45

Como este hilo es el principal resultado de Google para este tipo de preguntas, pensé que proporcionaría la solución que funcionó para mí. Ya estaba usando AFNetworking , pero la búsqueda no reveló cómo realizar esta tarea con AFNetworking hasta la mitad de mi proyecto.

Lo que quieres es el AFNetworkingReachabilityManager .

// -- Start monitoring network reachability (globally available) -- //
[[AFNetworkReachabilityManager sharedManager] startMonitoring];

[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {

    NSLog(@"Reachability changed: %@", AFStringFromNetworkReachabilityStatus(status));


    switch (status) {
        case AFNetworkReachabilityStatusReachableViaWWAN:
        case AFNetworkReachabilityStatusReachableViaWiFi:
            // -- Reachable -- //
            NSLog(@"Reachable");
            break;
        case AFNetworkReachabilityStatusNotReachable:
        default:
            // -- Not reachable -- //
            NSLog(@"Not Reachable");
            break;
    }

}];

También puede usar lo siguiente para probar la accesibilidad sincrónicamente (una vez que se ha iniciado la supervisión):

-(BOOL) isInternetReachable
{
    return [AFNetworkReachabilityManager sharedManager].reachable;
}

3
¡Voté por traerlo a la AFNetworkReachabilityManagerluz!
Selvin

Para aquellos para quienes esto no era inmediatamente obvio: AFNetworking es una biblioteca de terceros.
arlomedia

1
La accesibilidad a la red es una herramienta de diagnóstico que se puede utilizar para comprender por qué una solicitud puede haber fallado. No debe usarse para determinar si se realiza o no una solicitud. - Eso es lo que dice la documentación.
Nosov Pavel

AFNetworkReachabilityManager.reachableNO realiza ningún tipo de verificación sincrónica. Devuelve un lógico o de reachableViaWWANy reachableViaWifi.
jdolan 01 de

40

Perdón por responder demasiado tarde, pero espero que esta respuesta pueda ayudar a alguien en el futuro.

El siguiente es un pequeño fragmento de código C nativo que puede verificar la conectividad a Internet sin ninguna clase adicional.

Agregue los siguientes encabezados:

#include<unistd.h>
#include<netdb.h>

Código:

-(BOOL)isNetworkAvailable
{
    char *hostname;
    struct hostent *hostinfo;
    hostname = "google.com";
    hostinfo = gethostbyname (hostname);
    if (hostinfo == NULL){
        NSLog(@"-> no connection!\n");
        return NO;
    }
    else{
        NSLog(@"-> connection established!\n");
        return YES;
    }
}

Swift 3

func isConnectedToInternet() -> Bool {
    let hostname = "google.com"
    //let hostinfo = gethostbyname(hostname)
    let hostinfo = gethostbyname2(hostname, AF_INET6)//AF_INET6
    if hostinfo != nil {
        return true // internet available
      }
     return false // no internet
    }

3
funciona mucho mejor que la accesibilidad de Apple. Pero de acuerdo con los documentos gethostbyname está obsoleto y debe usar getaddrinfo: struct addrinfo * res = NULL; int s = getaddrinfo ("apple.com", NULL, NULL, & res); bool network_ok = (s == 0 && res! = NULL); freeaddrinfo (res);
Tertium

1
Aunque es común usar google.com para verificar la disponibilidad de Internet, vale la pena señalar que está bloqueado en algunos países. Una mejor opción sería dominios de compañías más "amigables con el gobierno" como yahoo.com o microsoft.com.
Laurent

8
@Laurent: la mejor opción es utilizar la dirección del servidor desde la que va a solicitar datos. google.com por aquí es solo un ejemplo.
despacho

77
¿Esto solo está haciendo una búsqueda de DNS? Si es así, ¿podría un resultado DNS en caché podría dar lugar a que la función crea falsamente que la red está disponible?
Stephen Moore

2
@amar Ejecutar esto en un bucle consumirá datos inalámbricos, no desea hacer eso.
significado-asuntos

31

Actualmente utilizo este método síncrono simple que no requiere archivos adicionales en sus proyectos o delegados.

Importar:

#import <SystemConfiguration/SCNetworkReachability.h>

Crea este método:

+(bool)isNetworkAvailable
{
    SCNetworkReachabilityFlags flags;
    SCNetworkReachabilityRef address;
    address = SCNetworkReachabilityCreateWithName(NULL, "www.apple.com" );
    Boolean success = SCNetworkReachabilityGetFlags(address, &flags);
    CFRelease(address);

    bool canReach = success
                    && !(flags & kSCNetworkReachabilityFlagsConnectionRequired)
                    && (flags & kSCNetworkReachabilityFlagsReachable);

    return canReach;
}

Entonces, si has puesto esto en un MyNetworkClass:

if( [MyNetworkClass isNetworkAvailable] )
{
   // do something networky.
}

Si está probando en el simulador, encienda y apague el wifi de su Mac, ya que parece que el simulador ignorará la configuración del teléfono.

Actualizar:

  1. Al final, utilicé un hilo / devolución de llamada asincrónica para evitar bloquear el hilo principal; y volver a probar regularmente para poder usar un resultado en caché, aunque debe evitar mantener las conexiones de datos abiertas innecesariamente

  2. Como @thunk describió, hay mejores URL para usar, que Apple usa. http://cadinc.com/blog/why-your-apple-ios-7-device-wont-connect-to-the-wifi-network


1
Gracias, y lo mismo que la mayoría de los demás en este hilo: no olvide agregar SystemConfiguration.framework a su proyecto.
eselk

1
Extraño, en mi proyecto Xcode, no tuve que agregar el SystemConfiguration.framework. Tengo SpriteKit, UIKit, StoreKit, Foundation y CoreGraphics, pero no SystemConfiguration ... ¿Hay alguna inclusión implícita?
GilesDMiddleton

3
Esto funcionó para mí, pero cuando lo ejecuté cuando estaba conectado a una red wi-fi que no tiene conexión a Internet, bloqueó mi interfaz de usuario durante unos 30 segundos. Se necesita un enfoque asincrónico en esa situación.
arlomedia

2
www.apple.comes un gran sitio web ... la mejor opción seráwww.google.com
Fahim Parkar

2
excelente respuesta! Una sugerencia: la URL de prueba, www.appleiphonecell.comsupuestamente está disponible estrictamente para este propósito y aparentemente no está bloqueada en China.
Thunk

11

Es posible y es realmente simple si lo mira al finalizar la implementación, lo cual es nuevamente, muy simple, ya que los únicos elementos que necesita son dos variables booleanas: acceso a internet y acceso al host (a menudo necesita más de uno de estos ) Una vez que ensambla su clase auxiliar que puede determinar el estado de las conexiones, realmente ya no le importa la implementación necesaria para conocer estos procedimientos.

Ejemplo:

#import <Foundation/Foundation.h>

@class Reachability;

@interface ConnectionManager : NSObject {
    Reachability *internetReachable;
    Reachability *hostReachable;
}

@property BOOL internetActive;
@property BOOL hostActive;

- (void) checkNetworkStatus:(NSNotification *)notice;

@end

Y el archivo .m:

#import "ConnectionManager.h"
#import "Reachability.h"

@implementation ConnectionManager
@synthesize internetActive, hostActive;

-(id)init {
    self = [super init];
    if(self) {

    }
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];

    internetReachable = [[Reachability reachabilityForInternetConnection] retain];
    [internetReachable startNotifier];

    hostReachable = [[Reachability reachabilityWithHostName:@"www.apple.com"] retain];
    [hostReachable startNotifier];

    return self;
}

- (void) checkNetworkStatus:(NSNotification *)notice {
    NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
    switch (internetStatus)

    {
        case NotReachable:
        {
            NSLog(@"The internet is down.");
            self.internetActive = NO;

            break;

        }
        case ReachableViaWiFi:
        {
            NSLog(@"The internet is working via WIFI.");
            self.internetActive = YES;

            break;

        }
        case ReachableViaWWAN:
        {
            NSLog(@"The internet is working via WWAN.");
            self.internetActive = YES;

            break;

        }
    }

    NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
    switch (hostStatus)

    {
        case NotReachable:
        {
            NSLog(@"A gateway to the host server is down.");
            self.hostActive = NO;

            break;

        }
        case ReachableViaWiFi:
        {
            NSLog(@"A gateway to the host server is working via WIFI.");
            self.hostActive = YES;

            break;

        }
        case ReachableViaWWAN:
        {
            NSLog(@"A gateway to the host server is working via WWAN.");
            self.hostActive = YES;

            break;

        }
    }

}

// If lower than SDK 5 : Otherwise, remove the observer as pleased.

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [super dealloc];
}

@end


4

Extraje el código y lo puse en un solo método, espero que ayude a otros.

#import <SystemConfiguration/SystemConfiguration.h>

#import <netinet/in.h>
#import <netinet6/in6.h>

...

- (BOOL)isInternetReachable
{    
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));
    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;

    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
    SCNetworkReachabilityFlags flags;

    if(reachability == NULL)
        return false;

    if (!(SCNetworkReachabilityGetFlags(reachability, &flags)))
        return false;

    if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
        // if target host is not reachable
        return false;


    BOOL isReachable = false;


    if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
    {
        // if target host is reachable and no connection is required
        //  then we'll assume (for now) that your on Wi-Fi
        isReachable = true;
    }


    if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
         (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
    {
        // ... and the connection is on-demand (or on-traffic) if the
        //     calling application is using the CFSocketStream or higher APIs

        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
        {
            // ... and no [user] intervention is needed
            isReachable = true;
        }
    }

    if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
    {
        // ... but WWAN connections are OK if the calling application
        //     is using the CFNetwork (CFSocketStream?) APIs.
        isReachable = true;
    }


    return isReachable;


}

[A] ¿De dónde extrajo este código? ¿Puedes publicar un enlace a la fuente? [B] ¡Muchas gracias por su trabajo de extracción! Justo lo que necesitaba. Estaba a punto de realizar esta tarea yo mismo.
Basil Bourque

Entonces, ¿parece que esta es la única respuesta que no verifica ningún servidor remoto?
Jasper

Para otros que prueben esto en MacOS, tenga en cuenta que kSCNetworkReachabilityFlagsIsWWAN es para OS_IPHONE y no para macOS.
GeoffCoope

4

Estoy escribiendo la versión rápida de la respuesta aceptada aquí , en caso de que alguien lo encuentre útil, el código se escribe rápido 2,

Puede descargar los archivos necesarios de SampleCode

Agrega Reachability.hy Reachability.marchiva a tu proyecto,

Ahora será necesario crear un Bridging-Header.harchivo si no existe ninguno para su proyecto,

Dentro de su Bridging-Header.harchivo agregue esta línea:

#import "Reachability.h"

Ahora para verificar la conexión a Internet

static func isInternetAvailable() -> Bool {
    let networkReachability : Reachability = Reachability.reachabilityForInternetConnection()
    let networkStatus : NetworkStatus = networkReachability.currentReachabilityStatus()

    if networkStatus == NotReachable {
        print("No Internet")
        return false
    } else {
        print("Internet Available")
        return true
    }

}

3

Creo que esto podría ayudar ...

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

if([AFNetworkReachabilityManager sharedManager].isReachable)
{
    NSLog(@"Network reachable");
}
else
{   
   NSLog(@"Network not reachable");
}

Esto no funciona si no se cambia la interfaz de red y se pierde la conectividad a Internet.
Pratik Somaiya

2

También puede probar este si ya configuró AFNetworking en su proyecto.

-(void)viewDidLoad{  // -- add connectivity notification --//
[[NSNotificationCenter defaultCenter ] addObserver:self selector:@selector(ReachabilityDidChangeNotification:) name:AFNetworkingReachabilityDidChangeNotification object:nil];}
-(void)ReachabilityDidChangeNotification:(NSNotification *)notify
{
// -- NSLog(@"Reachability changed: %@", AFStringFromNetworkReachabilityStatus(status));  -- //
NSDictionary *userInfo =[notif userInfo];
AFNetworkReachabilityStatus status= [[userInfo valueForKey:AFNetworkingReachabilityNotificationStatusItem] intValue];
switch (status)
{
    case AFNetworkReachabilityStatusReachableViaWWAN:
    case AFNetworkReachabilityStatusReachableViaWiFi:
        // -- Reachable -- //
// -- Do your stuff when internet connection is available -- //
        [self getLatestStuff];
        NSLog(@"Reachable");
        break;
    case AFNetworkReachabilityStatusNotReachable:
    default:
        // -- Not reachable -- //
        // -- Do your stuff for internet connection not available -- //
NSLog(@"Not Reachable");
        break;
}
}

1

Aquí hay una buena solución para verificar la conectividad usando Swift, sin usar Accesibilidad. Lo encontré en este blog .

Cree un nuevo archivo Swift en su proyecto llamado Network.swift(por ejemplo). Pegue este código dentro de ese archivo:

import Foundation

public class Network {

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

Luego puede verificar la conectividad en cualquier parte de su proyecto utilizando:

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

0

EDITAR: Esto no funcionará para las URL de red (ver comentarios)

A partir de iOS 5, hay un nuevo método de instancia NSURL:

- (BOOL)checkResourceIsReachableAndReturnError:(NSError **)error

Apúntelo al sitio web que le interesa o apúntelo a apple.com; Creo que es la nueva llamada de una línea para ver si Internet está funcionando en su dispositivo.


En realidad, en mis propias pruebas, esto siempre devuelve falso. Los documentos indican que este es el caso para 4.x pero en 5+ debería estar funcionando. YMMV
SG1

Esto no parece funcionar para la situación que está buscando. Ver stackoverflow.com/questions/9266154/…
Micah Hainline

77
Eso se usa para URL de archivos, no URL de Internet.
Victor Bogdan

0

Tampoco estaba satisfecho con las opciones de comprobación de Internet disponibles (¿Por qué no es una API nativa?!?!)

Mi propio problema fue con la pérdida de paquetes del 100%: cuando un dispositivo está conectado al enrutador, pero el enrutador no está conectado a Internet. La accesibilidad y otros colgarán por siglos. Creé una clase singleton de utilidad para lidiar con eso agregando un tiempo de espera asíncrono. Funciona bien en mi aplicación. Espero eso ayude. Aquí está el enlace en github:

https://github.com/fareast555/TFInternetChecker


0

Comprobación de la disponibilidad de conexión a Internet en (iOS) Xcode 8.2, Swift 3.0

Este es un método simple para verificar la disponibilidad de la red. Logré traducirlo a Swift 2.0 y aquí el código final. La clase de accesibilidad de Apple existente y otras bibliotecas de terceros parecían ser demasiado complicadas para traducir a Swift.

Esto funciona tanto para conexiones 3G como WiFi.

No olvide agregar "SystemConfiguration.framework" a su generador de proyectos.

//Create new swift class file Reachability in your project.

import SystemConfiguration

public 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) {
        $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)
   }
}

// Check network connectivity from anywhere in project by using this code.

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

Con Xcode versión 7.2 (7C68), este código tiene errores de compilación
Daniel

@Daniel, gracias por señalar el problema, esto debido a punteros inseguros, ya que Swift 2 / Xcode está actualizando sus sintaxis, debemos seguirlos. He actualizado el código para el mismo. ty;)
ViJay Avhad

Si está conectado a una red wi-fi pero no puede conectarse a Internet (abra una página de google), aún así se vuelve verdadero para alcanzar. Está incorrecto.
Arildo Junior

0

Reemplazo para la accesibilidad de Apple reescrito en Swift con cierres , inspirado en tonymillion: https://github.com/ashleymills/Reachability.swift

  1. Suelta el archivo Reachability.swiften tu proyecto. Alternativamente, use CocoaPods o Carthage : consulte la sección Instalación del archivo README del proyecto.

  2. Recibe notificaciones sobre conectividad de red:

    //declare this property where it won't go out of scope relative to your listener
    let reachability = Reachability()!
    
    reachability.whenReachable = { reachability in
        if reachability.isReachableViaWiFi {
            print("Reachable via WiFi")
        } else {
            print("Reachable via Cellular")
        }
    }
    
    reachability.whenUnreachable = { _ in
        print("Not reachable")
    }
    
    do {
        try reachability.startNotifier()
    } catch {
        print("Unable to start notifier")
    }

    y para detener las notificaciones

    reachability.stopNotifier()

0

Alamofire

Si ya está usando Alamofire para toda la API RESTful, esto es lo que puede beneficiar de eso.

Puede agregar la siguiente clase a su aplicación y llamar MNNetworkUtils.main.isConnected()para obtener un booleano sobre si está conectado o no.

#import Alamofire

class MNNetworkUtils {
  static let main = MNNetworkUtils()
  init() {
    manager = NetworkReachabilityManager(host: "google.com")
    listenForReachability()
  }

  private let manager: NetworkReachabilityManager?
  private var reachable: Bool = false
  private func listenForReachability() {
    self.manager?.listener = { [unowned self] status in
      switch status {
      case .notReachable:
        self.reachable = false
      case .reachable(_), .unknown:
        self.reachable = true
      }
    }
    self.manager?.startListening()
  }

  func isConnected() -> Bool {
    return reachable
  }
}

Esta es una clase singleton. Cada vez, cuando el usuario conecta o desconecta la red, se anulará self.reachablea verdadero / falso correctamente, porque comenzamos a escuchar la NetworkReachabilityManagerinicialización de singleton.

También para monitorear la accesibilidad, debe proporcionar un host, actualmente estoy usando no google.comdude en cambiar a cualquier otro host o uno de los suyos si es necesario.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.