A maneira mais fácil de detectar conexão com a Internet no iOS?


147

Sei que essa pergunta parecerá um engodo de muitas outras, no entanto, não acho que o caso simples seja bem explicado aqui. Vindo de um plano de fundo Android e BlackBerry, fazer solicitações através de HTTPUrlConnectionfalha instantânea se não houver conexão disponível. Parece um comportamento completamente sensato, e fiquei surpreso ao descobrir que NSURLConnectionno iOS não o emulei.

Entendo que a Apple (e outras pessoas que a estenderam) forneça uma Reachabilityclasse para ajudar na determinação do estado da rede. Fiquei feliz em ver isso pela primeira vez e esperava ver algo parecido bool isNetworkAvailable(), mas, para minha surpresa, encontrei um sistema complexo que exigia registros de notificação e retornos de chamada, além de vários detalhes aparentemente desnecessários. Deve haver uma maneira melhor.

Meu aplicativo já lida com falhas de conexão, incluindo nenhuma conectividade. O usuário é notificado da falha e o aplicativo segue em frente.

Portanto, meus requisitos são simples: Função única e síncrona que eu posso chamar antes de todas as solicitações HTTP para determinar se devo realmente enviar a solicitação ou não. Idealmente, não requer configuração e retorna apenas um booleano.

Isso realmente não é possível no iOS?



Você nunca deve chamar um método de acessibilidade de rede síncrona antes de cada solicitação HTTP; é meio doido fazer esse trabalho, a menos que a acessibilidade assine de forma assíncrona que houve uma alteração nas condições da rede e você poderá optar por não enviar a solicitação HTTP. Esse também é o motivo pelo qual os tempos limite existem (e você deve lidar com esse cenário normalmente). Veja o link acima para a maneira assíncrona
iwasrobbed

Respostas:


251

Eu fiz um pouco mais de pesquisa e estou atualizando minha resposta com uma solução mais atual. Não tenho certeza se você já olhou para ele, mas há um bom código de exemplo fornecido pela Apple.

Faça o download do código de exemplo aqui

Inclua os arquivos Reachability.he Reachability.m em seu projeto. Dê uma olhada em ReachabilityAppDelegate.m para ver um exemplo de como determinar a acessibilidade do host, a acessibilidade por WiFi, pela WWAN etc. Para uma verificação muito simples da acessibilidade da rede, você pode fazer algo como isto

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: Não se esqueça de adicionar SystemConfiguration.framework ao seu projeto.


46
Não se esqueça de adicionar SystemConfiguration.framework ao seu projeto.
Benjamin Piette

1
@chandru Funciona. Essa é a maneira mais fácil e eficaz de detectar a conexão de rede. Simples e Fácil!
S1U

3
Cuidado com isso. Estou descobrindo que não funciona de maneira confiável quando a rede é restaurada, pelo menos no simulador. Eu inicio com o wifi desligado e ele informa corretamente que não há rede disponível; mas ligo o wifi novamente, e SCNetworkReachabilityGetFlags continua retornando 0, mesmo que as consultas reais da rede funcionem perfeitamente.
precisa saber é o seguinte

2
Acredito que minha resposta abaixo ( stackoverflow.com/a/26593728/557362 ) requer menos trabalho (não é necessário baixar outros arquivos), mas essa é apenas a minha opinião.
precisa saber é o seguinte

3
Observe que isso apenas informa se a conexão com o host específico é roteável . Isso não significa que você é realmente capaz de se conectar. Exemplo: você conecta o wifi do seu iPhone ao wifi da sua câmera. O Wi-Fi está conectado a um gateway padrão: todos os hosts reportam-se acessíveis. Mas na verdade não são - a câmera é uma conexão sem saída.
xaphod

45

Como esse segmento é o principal resultado do Google para esse tipo de pergunta, achei que forneceria a solução que funcionou para mim. Eu já estava usando o AFNetworking , mas a pesquisa não revelou como realizar essa tarefa com o AFNetworking até a metade do meu projeto.

O que você deseja é o 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;
    }

}];

Você também pode usar o seguinte para testar a acessibilidade de forma síncrona (depois que o monitoramento for iniciado):

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

3
Eu votei por trazer a AFNetworkReachabilityManagerluz!
Selvin

Para aqueles a quem isso não foi imediatamente óbvio: o AFNetworking é uma biblioteca de terceiros.
Arlomedia 21/05

1
A acessibilidade da rede é uma ferramenta de diagnóstico que pode ser usada para entender por que uma solicitação pode ter falhado. Não deve ser usado para determinar se deve ou não fazer uma solicitação. - É o que a documentação diz.
Nosov Pavel

AFNetworkReachabilityManager.reachableNÃO realiza nenhum tipo de verificação síncrona. Retorna um lógico ou de reachableViaWWANe reachableViaWifi.
Jdolan #

40

Desculpe por responder tarde demais, mas espero que esta resposta possa ajudar alguém no futuro.

A seguir, um pequeno trecho de código C nativo que pode verificar a conectividade com a Internet sem nenhuma classe extra.

Adicione os seguintes cabeçalhos:

#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 muito melhor que a acessibilidade da Apple. Mas, de acordo com a documentação, gethostbyname é obsoleto e você deve 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
Embora seja comum usar o google.com para verificar a disponibilidade da Internet, é importante notar que ele está bloqueado em alguns países. Uma escolha melhor seria domínios de empresas mais "amigas do governo", como yahoo.com ou microsoft.com.
laurent

8
@ Laurent: A melhor opção é usar o endereço do servidor do qual você deseja solicitar dados. google.com aqui é apenas um exemplo.
dispatchMain

7
Isso está apenas fazendo uma pesquisa de DNS? Em caso afirmativo, um resultado DNS armazenado em cache pode resultar na função de acreditar falsamente que a rede está disponível?
Stephen Moore

2
@amar Executar isso em loop consumirá dados sem fio, você não deseja fazer isso.
significado-importa

31

Atualmente, uso esse método síncrono simples que não requer arquivos extras em seus projetos ou delegados.

Importar:

#import <SystemConfiguration/SCNetworkReachability.h>

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

Então, se você colocou isso em um MyNetworkClass:

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

Se você estiver testando no simulador, ligue e desligue o wifi do seu Mac, pois parece que o simulador ignorará a configuração do telefone.

Atualizar:

  1. No final, usei um retorno de chamada encadeado / assíncrono para evitar o bloqueio do encadeamento principal; e testando regularmente para que eu pudesse usar um resultado em cache - embora você deva evitar manter as conexões de dados desnecessariamente abertas.

  2. Como o @thunk descreveu, existem URLs melhores para usar, que a própria Apple usa. http://cadinc.com/blog/why-your-apple-ios-7-device-wont-connect-to-the-wifi-network


1
Obrigado e o mesmo que a maioria dos outros neste segmento: Não se esqueça de adicionar SystemConfiguration.framework ao seu projeto.
eselk

1
Estranho, no meu projeto do Xcode, não precisei adicionar o SystemConfiguration.framework. Eu tenho SpriteKit, UIKit, StoreKit, Foundation e CoreGraphics, mas não SystemConfiguration .... Existe alguma inclusão implícita em andamento?
GilesDMiddleton

3
Isso funcionou para mim, mas quando o executei quando conectado a uma rede wi-fi sem conexão à Internet, ele bloqueou minha interface do usuário por cerca de 30 segundos. Uma abordagem assíncrona é necessária nessa situação.
Arlomedia 21/05

2
www.apple.comé grande o website ... a opção melhor seráwww.google.com
Fahim Parkar

2
excelente resposta! Uma sugestão: o URL de teste,, www.appleiphonecell.comsupostamente está disponível estritamente para esse fim e, aparentemente, não está bloqueado na China.
Thunk

11

É possível e é realmente simples se você olhar para ela ao concluir a implementação, o que é novamente - muito simples, pois os únicos itens necessários são duas variáveis ​​booleanas: acessibilidade na Internet e acessibilidade no host (geralmente você precisa de mais de um deles). ) Depois de montar sua classe auxiliar que pode determinar o status das conexões, você realmente não se importa mais com a implementação necessária para conhecer esses procedimentos.

Exemplo:

#import <Foundation/Foundation.h>

@class Reachability;

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

@property BOOL internetActive;
@property BOOL hostActive;

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

@end

E o arquivo .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

Eu extraí o código e coloquei em um único método, espero que ajude outros.

#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 onde você extraiu esse código? Você pode postar um link para a fonte? [B] Muito obrigado pelo seu trabalho de extração! Apenas o que eu precisava. Eu estava prestes a realizar essa tarefa sozinho.
Basil Bourque

Então, parece que esta é a única resposta que não verifica nenhum servidor remoto?
Jasper

Para outras pessoas testando isso no MacOS, observe que kSCNetworkReachabilityFlagsIsWWAN é para OS_IPHONE e não para macOS.
GeoffCoope 16/09/17

4

Estou escrevendo a versão rápida da resposta aceita aqui , caso alguém ache útil, o código está escrito rapidamente 2,

Você pode baixar os arquivos necessários do SampleCode

Adicione Reachability.he Reachability.marquive no seu projeto,

Agora, será necessário criar um Bridging-Header.harquivo, se não houver nenhum para o seu projeto,

Dentro do seu Bridging-Header.harquivo, adicione esta linha:

#import "Reachability.h"

Agora, a fim de verificar a conexão com 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

Eu acho que isso poderia ajudar ..

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

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

Isso não funciona se a interface de rede não for alterada e a conectividade com a Internet for perdida.
Pratik Somaiya

2

Você também pode tentar este caso já tenha configurado o AFNetworking em seu projeto.

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

Aqui está uma boa solução para verificar a conectividade usando o Swift, sem usar a acessibilidade. Encontrei-o neste blog .

Crie um novo arquivo Swift no seu projeto chamado Network.swift(por exemplo). Cole este código dentro desse arquivo:

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

Em seguida, você pode verificar a conectividade em qualquer lugar do seu projeto usando:

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

0

EDIT: Isso não funcionará para URLs da rede (ver comentários)

No iOS 5, existe um novo método de instância NSURL:

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

Aponte para o site de seu interesse ou para apple.com; Eu acho que é a nova chamada de uma linha para ver se a Internet está funcionando no seu dispositivo.


Na verdade, em meus próprios testes, isso sempre retorna falso. Os documentos afirmam que esse é o caso do 4.x, mas no 5+ ele deve estar funcionando. YMMV
SG1

Isso não parece funcionar para a situação que você está procurando. Veja stackoverflow.com/questions/9266154/…
Micah Hainline

7
Isso é usado para URLs de arquivos, não para URLs da Internet.
Victor Bogdan

0

Também não estava satisfeito com as opções de verificação da Internet disponíveis (por que essa API não é nativa?!?!)

Meu próprio problema foi com 100% de perda de pacotes - quando um dispositivo está conectado ao roteador, mas o roteador não está conectado à Internet. A acessibilidade e outros permanecerão suspensos por muito tempo. Eu criei uma classe de utilitário singleton para lidar com isso, adicionando um tempo limite assíncrono. Funciona bem no meu aplicativo. Espero que ajude. Aqui está o link no github:

https://github.com/fareast555/TFInternetChecker


0

Verificando a disponibilidade da conexão com a Internet no (iOS) Xcode 8.2, Swift 3.0

Este é um método simples para verificar a disponibilidade da rede. Consegui traduzi-lo para o Swift 2.0 e aqui o código final. A classe de alcançabilidade da Apple existente e outras bibliotecas de terceiros pareciam ser muito complicadas para serem traduzidas para o Swift.

Isso funciona para conexões 3G e WiFi.

Não se esqueça de adicionar "SystemConfiguration.framework" ao seu construtor de projetos.

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

Com o Xcode versão 7.2 (7C68), esse código tem erros de compilador
Daniel

@ Daniel, obrigado por apontar o problema, isso por causa de indicadores inseguros, como o Swift 2 / Xcode está atualizando as sintaxes, devemos segui-las. Eu atualizei o código para o mesmo. ty;)
ViJay Avhad

Se você estiver conectado a uma rede wi-fi, mas não conseguir se conectar à internet (abra uma página do google), ele retornará verdadeiro para alcançável. Está errado.
Arildo Junior 02/02

0

Substituição da acessibilidade da Apple reescrita em Swift com fechamentos , inspirada no tonymillion: https://github.com/ashleymills/Reachability.swift

  1. Solte o arquivo Reachability.swiftem seu projeto. Como alternativa, use CocoaPods ou Carthage - consulte a seção Instalação do README do projeto.

  2. Receba notificações sobre conectividade de rede:

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

    e para interromper as notificações

    reachability.stopNotifier()

0

Alamofire

Se você já está usando o Alamofire para todas as APIs RESTful, aqui está o que você pode se beneficiar disso.

Você pode adicionar a seguinte classe ao seu aplicativo e ligar MNNetworkUtils.main.isConnected()para obter um valor booleano se está conectado ou não.

#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 é uma classe singleton. Sempre que o usuário conectar ou desconectar a rede, ela será substituída self.reachablepor verdadeira / falsa corretamente, porque começamos a ouvir a NetworkReachabilityManagerinicialização on singleton.

Também para monitorar a acessibilidade, você precisa fornecer um host. Atualmente, estou usando a google.comvontade para mudar para outros hosts ou um dos seus, se necessário.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.