porque - La forma más fácil de detectar la conexión a Internet en iOS?
no puedo actualizar mi iphone 6 (16)
Sé que esta pregunta parecerá una trampa para muchos otros, sin embargo, no creo que el caso simple esté bien explicado aquí. Procedente de un fondo de Android y BlackBerry, las solicitudes a través de HTTPUrlConnection
fallan instantáneamente si no hay conexión disponible. Esto parece un comportamiento completamente sensato, y me sorprendió descubrir que NSURLConnection
en iOS no lo emulaba.
Entiendo que Apple (y otras personas que lo han extendido) ofrecen una clase de Reachability
para ayudar a determinar el estado de la red. Me alegró ver esto por primera vez y esperé ver algo como bool isNetworkAvailable()
, pero para mi sorpresa encontré un sistema complejo que requería registros de notificación y devoluciones de llamada, 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 única y sincrónica a la que puedo llamar antes de todas las solicitudes HTTP para determinar si realmente me molesto en enviar la solicitud o no. Idealmente, no requiere configuración y simplemente devuelve un valor booleano.
¿Esto realmente no es posible en iOS?
Alamofire
Si ya está utilizando Alamofire para todos los RESTful Api, aquí está lo que puede beneficiarse de eso.
Puede agregar la siguiente clase a su aplicación y llamar a MNNetworkUtils.main.isConnected()
para obtener un booleano, ya sea que 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. En todo momento, cuando el usuario se conecta o desconecta de la red, anulará el self.reachable
de self.reachable
en verdadero / falso correctamente, porque comenzamos a escuchar el NetworkReachabilityManager
en la inicialización de singleton.
Además, para controlar la accesibilidad, debe proporcionar un host. En este momento estoy usando google.com
Siéntase libre de cambiar a cualquier otro host o uno de los suyos si es necesario.
Actualmente uso este método simple sincrónico 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 MyNetworkClass
:
if( [MyNetworkClass isNetworkAvailable] )
{
// do something networky.
}
Si está realizando pruebas 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:
Al final usé un hilo / devolución de llamada asincrónica para evitar el bloqueo del hilo principal; y volver a realizar pruebas regularmente para poder utilizar un resultado en caché, aunque debe evitar mantener las conexiones de datos abiertas innecesariamente.
Como se describe en @thunk, hay mejores URL para usar, que Apple usa. http://cadinc.com/blog/why-your-apple-ios-7-device-wont-connect-to-the-wifi-network
Alguien ha resuelto esto de una manera simple y reutilizable antes. DDGReachability
.
EDITAR: O tonymillion/Reachability
.
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 lugar de su proyecto al usar:
if Network.isConnectedToNetwork() == true {
println("Internet connection OK")
} else {
println("Internet connection FAILED")
}
Creo que esto podría ayudar ...
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
if([AFNetworkReachabilityManager sharedManager].isReachable)
{
NSLog(@"Network reachable");
}
else
{
NSLog(@"Network not reachable");
}
Es posible y es muy simple si lo miras al finalizar la implementación, que es de nuevo, muy simple, ya que los únicos elementos que necesitas son dos variables booleanas: accesibilidad de internet y accesibilidad del host (a menudo necesitas más de uno de estos ) Una vez que ensambla su clase de ayudante que puede determinar el estado de las conexiones, realmente 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
Estoy escribiendo la versión rápida de la respuesta aceptada here , en caso de que alguien lo encuentre útil, el código se escribe rápido 2,
Puede descargar los archivos necesarios desde here
Agregue los Reachability.h
Reachability.m
a su proyecto,
Ahora uno necesitará crear Bridging-Header.h
archivo Bridging-Header.h
si no existe ninguno para su proyecto.
Dentro de su archivo Bridging-Header.h
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
}
}
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;
}
Hice un poco más de investigación 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 here
Incluya los archivos Reachability.hy Reachability.m en su proyecto. Eche un vistazo a ReachabilityAppDelegate.m para ver un ejemplo sobre cómo determinar la accesibilidad del host, accesibilidad por WiFi, por WWAN, etc. Para una verificación muy simple 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: No olvides agregar SystemConfiguration.framework a tu proyecto.
Lamento haber respondido demasiado tarde, pero espero que esta respuesta pueda ayudar a alguien en el futuro.
Lo que sigue 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
}
Reemplazo de Reachability de Apple reescrito en Swift con cierres , inspirado en tonymillion: https://github.com/ashleymills/Reachability.swift
Reachability.swift
el archivoReachability.swift
en su proyecto. Alternativamente, use CocoaPods o Carthage - Consulte la sección Instalación del CocoaPods README del proyecto.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 notificaciones
reachability.stopNotifier()
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;
}
}
Tampoco estaba contento 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 al 100%: cuando un dispositivo está conectado al enrutador, pero el enrutador no está conectado a Internet. Accesibilidad y otros colgarán por siglos. Creé una clase singleton de utilidad para manejar eso agregando un tiempo de espera de asincronización. Funciona bien en mi aplicación. Espero eso ayude. Aquí está el enlace en github:
Viendo que este hilo es el resultado principal de google para este tipo de pregunta, pensé que podría proporcionar la solución que funcionó para mí. Ya estaba usando AFNetworking , pero la búsqueda no reveló cómo lograr esta tarea con AFNetworking hasta la mitad de mi proyecto.
Lo que quiere es 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 de forma síncrona (una vez que se ha iniciado la monitorización):
-(BOOL) isInternetReachable
{
return [AFNetworkReachabilityManager sharedManager].reachable;
}
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 Apple Reachability 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 creador 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")
}
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
Dirígete al sitio web que te interesa o apunta a apple.com; Creo que es la nueva llamada de una línea para ver si Internet está funcionando en su dispositivo.