Самый простой способ обнаружить интернет-соединение на iOS? - PullRequest
146 голосов
/ 11 января 2012

Я знаю, что этот вопрос будет обманом многих других, однако я не чувствую, что простой случай хорошо объяснен здесь. Исходя из Android и BlackBerry, отправка запросов через HTTPUrlConnection мгновенно завершается сбоем, если нет доступного соединения. Это похоже на совершенно нормальное поведение, и я был удивлен, обнаружив, что NSURLConnection в iOS не эмулирует его.

Я понимаю, что Apple (и другие, кто ее расширил) предоставляют класс Reachability для определения состояния сети. Я был счастлив сначала увидеть это и полностью ожидал увидеть что-то вроде bool isNetworkAvailable(), но вместо этого к своему удивлению я обнаружил сложную систему, требующую регистрации уведомлений и обратных вызовов, и кучу, казалось бы, ненужных деталей. Должен быть лучший способ.

Мое приложение уже корректно обрабатывает сбои подключения, включая отсутствие подключения. Пользователь уведомляется о сбое, и приложение перемещается.

Таким образом, мои требования просты: одиночная синхронная функция, которую я могу вызвать перед всеми HTTP-запросами, чтобы определить, стоит ли мне беспокоиться об отправке запроса или нет. В идеале он не требует настройки и просто возвращает логическое значение.

Это действительно невозможно на iOS?

Ответы [ 16 ]

249 голосов
/ 11 января 2012

Я провел немного больше исследований и обновляю свой ответ более актуальным решением.Я не уверен, что вы уже посмотрели его, но есть хороший пример кода, предоставленный Apple.

Загрузите образец кода здесь

Включите файлы Reachability.h и Reachability.m в свой проект.Взгляните на ReachabilityAppDelegate.m, чтобы увидеть пример того, как определить достижимость хоста, доступность по WiFi, WWAN и т. Д. Для очень простой проверки доступности сети вы можете сделать что-то вроде

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: не забудьте добавить SystemConfiguration.framework в ваш проект.

45 голосов
/ 30 марта 2014

Видя, что эта тема является лучшим результатом Google для этого типа вопроса, я решил, что предоставлю решение, которое сработало для меня.Я уже использовал AFNetworking , но поиск не показал, как выполнить эту задачу с AFNetworking, до середины моего проекта.

То, что вам нужно, это 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;
    }

}];

Вы также можете использовать следующее для синхронной проверки достижимости (после начала мониторинга):

-(BOOL) isInternetReachable
{
    return [AFNetworkReachabilityManager sharedManager].reachable;
}
39 голосов
/ 14 февраля 2013

Извините за слишком поздний ответ, но я надеюсь, что этот ответ может кому-то помочь в будущем.

Ниже приведен небольшой фрагмент кода C, который может проверять подключение к Интернету без каких-либо дополнительных классов.

Добавитьследующие заголовки:

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

Код:

-(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
    }
31 голосов
/ 27 октября 2014

В настоящее время я использую этот простой синхронный метод, который не требует дополнительных файлов в ваших проектах или делегатах.

Импорт:

#import <SystemConfiguration/SCNetworkReachability.h>

Создать этот метод:

+(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;
}

Тогда, если вы поместили это в MyNetworkClass:

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

Если вы проводите тестирование в симуляторе, включите и выключите Wi-Fi на вашем Mac, так как кажется, что симулятор проигнорирует настройки телефона.

Обновление:

  1. В конце я использовал поток / асинхронный обратный вызов, чтобы избежать блокировки основного потока; и регулярно повторять тестирование, чтобы я мог использовать кэшированный результат - хотя вы должны избегать ненужного открытия соединений с данными.

  2. Как описано @thunk, есть лучшие URL-адреса для использования, которые используют сами Apple. http://cadinc.com/blog/why-your-apple-ios-7-device-wont-connect-to-the-wifi-network

11 голосов
/ 11 января 2012

Это возможно, и это действительно просто, если вы посмотрите на это после завершения реализации, что опять-таки очень просто, поскольку вам нужны только две логические переменные: доступность интернета и доступность хоста (вам часто нужно больше, чем один из них). После того, как вы соберете свой вспомогательный класс, который может определять состояние соединений, вам больше не нужно заботиться о реализации, необходимой для знания этих процедур.

Пример:

#import <Foundation/Foundation.h>

@class Reachability;

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

@property BOOL internetActive;
@property BOOL hostActive;

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

@end

И файл .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
6 голосов
/ 11 января 2012

Кто-то уже решил эту проблему простым и многократно используемым способом. DDGReachability.

РЕДАКТИРОВАТЬ: Или tonymillion/Reachability.

4 голосов
/ 20 декабря 2012

Я извлек код и поместил его в один метод, надеюсь, он поможет другим.

#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;


}
3 голосов
/ 14 декабря 2015

Я пишу быструю версию принятого ответа здесь , если кто-то найдет его полезным, код будет написан быстро 2,

Вы можете загрузить необходимые файлы с SampleCode

Добавьте файл Reachability.h и Reachability.m в ваш проект,

Теперь нужно создать файл Bridging-Header.h, если его нет для вашего проекта,

Внутри вашего Bridging-Header.h файла добавьте эту строку:

#import "Reachability.h"

Теперь для проверки интернет-соединения

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 голосов
/ 20 октября 2015

Я думаю, что это может помочь ..

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

if([AFNetworkReachabilityManager sharedManager].isReachable)
{
    NSLog(@"Network reachable");
}
else
{   
   NSLog(@"Network not reachable");
}
2 голосов
/ 30 августа 2017

Вы также можете попробовать это, если вы уже настроили AFNetworking в своем проекте.

-(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;
}
}
...