Самый простой способ обнаружить интернет-соединение на iOS?

147

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

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

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

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

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

RealCasually
источник
Вы никогда не должны вызывать синхронный метод достижимости сети перед каждым HTTP-запросом; это просто безумие делать эту работу, если функция достижимости не сообщает асинхронно, что произошли изменения в условиях сети, и тогда вы можете не отправлять HTTP-запрос. Это также причина того, что тайм-ауты существуют (и тогда вы должны изящно обработать этот сценарий). См. Ссылку выше для асинхронного пути
iwasrobbed

Ответы:

251

Я провел немного больше исследований и обновляю свой ответ более актуальным решением. Я не уверен, что вы уже посмотрели его, но есть хороший пример кода, предоставленный 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 в ваш проект.

Semere Taézaz Sium
источник
46
Не забудьте добавить SystemConfiguration.framework в ваш проект.
Бенджамин Пиетт
1
@chandru Это работает. Это самый простой и эффективный способ обнаружения сетевого подключения. Просто и легко!
S1U
3
Осторожнее с этим. Я считаю, что он не работает надежно при восстановлении сети, по крайней мере, в симуляторе. Я запускаю с выключенным Wi-Fi, и он правильно сообщает о недоступности сети; но затем я снова включаю Wi-Fi, и SCNetworkReachabilityGetFlags продолжает возвращать 0, хотя реальные сетевые запросы работают просто отлично.
Джо Струт
2
Я считаю, что мой ответ ниже ( stackoverflow.com/a/26593728/557362 ) требует меньше работы (без загрузки других файлов), но это только мое мнение.
ДжайлсДиддлтон
3
Обратите внимание, что это говорит только о том, является ли соединение с конкретным хостом маршрутизируемым . Это не значит, что вы на самом деле можете подключиться. Пример: вы подключаете WiFi вашего iPhone к WiFi вашей камеры. Wi-Fi связан со шлюзом по умолчанию: все хосты сообщат о доступности. Но на самом деле это не так - камера является тупиковой связью.
xaphod
45

Видя, что эта тема является лучшим результатом 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;
}
sherbertman
источник
3
Я проголосовал за AFNetworkReachabilityManagerосвещение!
Сельвин
Для тех, кому это было не сразу очевидно: AFNetworking - сторонняя библиотека.
arlomedia
1
Достижимость сети - это диагностический инструмент, который можно использовать для понимания причины сбоя запроса. Он не должен использоваться, чтобы определить, следует ли делать запрос. - Это то, что говорит документация.
Носов Павел
AFNetworkReachabilityManager.reachableНЕ выполняет никакой синхронной проверки. Возвращает логическое или из reachableViaWWANи reachableViaWifi.
jdolan
40

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

Ниже приведен небольшой фрагмент кода 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
    }
dispatchMain
источник
3
работает намного лучше, чем Apple Reachability. Но, согласно документации, gethostbyname устарело, и вы должны использовать getaddrinfo: struct addrinfo * res = NULL; int s = getaddrinfo ("apple.com", NULL, NULL, & res); bool network_ok = (s == 0 && res! = NULL); freeaddrinfo (RES);
Tertium
1
Хотя обычно для проверки доступности Интернета используется google.com, стоит отметить, что в некоторых странах он заблокирован. Лучшим выбором были бы домены от более «дружественных правительству» компаний, таких как yahoo.com или microsoft.com.
Лоран
8
@ Laurent: лучший вариант - использовать адрес сервера, с которого вы собираетесь запрашивать данные. google.com здесь только пример.
рассылкаОпно
7
Это просто делает поиск DNS? Если да, может ли результат кэширования DNS привести к тому, что функция ошибочно полагает, что сеть доступна?
Стивен Мур
2
@amar Выполнение этого в цикле будет потреблять беспроводные данные, вы не хотите этого делать.
значение имеет значение
31

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

Импортировать:

#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

GilesDMiddleton
источник
1
Спасибо, как и большинство других в этой теме: не забудьте добавить SystemConfiguration.framework в ваш проект.
Eselk
1
Странно, но в моем проекте XCode мне не нужно было добавлять SystemConfiguration.framework. У меня есть SpriteKit, UIKit, StoreKit, Foundation и CoreGraphics, но не SystemConfiguration .... Есть ли какое-то неявное включение?
ДжайлсДиддлтон
3
Это сработало для меня, но когда я запустил его при подключении к сети Wi-Fi, у которой нет подключения к Интернету, он заблокировал мой пользовательский интерфейс примерно на 30 секунд. В этой ситуации необходим асинхронный подход.
arlomedia
2
www.apple.comбольшой сайт ... лучший вариант будетwww.google.com
Фахим Паркар
2
отличный ответ! Одно из предложений: тестовый URL, www.appleiphonecell.comпредположительно, доступен исключительно для этой цели и явно не заблокирован в Китае.
августа
11

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

Пример:

#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
Данут Пралеа
источник
4

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

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


}
Walty Yeung
источник
[A] Откуда вы взяли этот код? Можете ли вы опубликовать ссылку на источник? [B] Большое спасибо за вашу добычу! Как раз то, что мне было нужно. Я собирался выполнить эту работу сам.
Базилик Бурк
Таким образом, кажется, что это единственный ответ, который не проверяет какой-либо удаленный сервер (ы)?
Джаспер
Для других, проверяющих это на MacOS, обратите внимание, что kSCNetworkReachabilityFlagsIsWWAN для OS_IPHONE, а не для macOS.
GeoffCoope
4

Я пишу быструю версию принятого ответа здесь , если кто-то найдет его полезным, код написан быстро 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

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

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

if([AFNetworkReachabilityManager sharedManager].isReachable)
{
    NSLog(@"Network reachable");
}
else
{   
   NSLog(@"Network not reachable");
}
пуджа
источник
Это не работает, если сетевой интерфейс не изменен и подключение к Интернету потеряно.
Пратик
2

Вы также можете попробовать это, если вы уже настроили 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;
}
}
Рави Раджа Джангид
источник
1

Вот хорошее решение для проверки подключения с использованием Swift, без использования Reachability. Я нашел это в этом блоге .

Создайте новый файл Swift в вашем проекте с именем Network.swift(например). Вставьте этот код в этот файл:

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

Затем вы можете проверить подключение в любом месте вашего проекта, используя:

if Network.isConnectedToNetwork() == true {
    println("Internet connection OK")
} else {
    println("Internet connection FAILED")
}
blwinters
источник
0

РЕДАКТИРОВАТЬ: Это не будет работать для сетевых URL-адресов (см. Комментарии)

Начиная с iOS 5, существует новый метод экземпляра NSURL:

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

Направьте его на интересующий вас сайт или укажите на apple.com; Я думаю, что это новый однострочный звонок, чтобы увидеть, работает ли интернет на вашем устройстве.

SG1
источник
На самом деле в моих собственных тестах это всегда возвращает false. Документы утверждают, что это относится к 4.x, но в 5+ оно должно работать. YMMV
SG1
Это не похоже на ситуацию, которую вы ищете. См stackoverflow.com/questions/9266154/...
Михей Hainline
7
Это используется для файловых URL, а не для интернет-URL.
Виктор Богдан
0

Я также не был доволен доступными опциями проверки интернета (почему это не нативный API?!?!)

Моя собственная проблема была со 100% потерей пакетов - когда устройство подключено к маршрутизатору, но маршрутизатор не подключен к Интернету. Достижимость и другие будут зависать целую вечность. Я создал вспомогательный одноэлементный класс, чтобы справиться с этим, добавив время ожидания асинхронности. Это прекрасно работает в моем приложении. Надеюсь, поможет. Вот ссылка на github:

https://github.com/fareast555/TFInternetChecker

Майк Кричли
источник
0

Проверка доступности интернет-соединения в (iOS) Xcode 8.2, Swift 3.0

Это простой метод проверки доступности сети. Мне удалось перевести его на Swift 2.0 и вот окончательный код. Существующий класс Apple Reachability и другие сторонние библиотеки казались слишком сложными для перевода на Swift.

Это работает как для 3G, так и для WiFi соединений.

Не забудьте добавить «SystemConfiguration.framework» в ваш компоновщик проекта.

//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")
}
Виджай Авхад
источник
С версией Xcode 7.2 (7C68) этот код имеет ошибки компилятора
Daniel
@Daniel, спасибо за указание на проблему, это из-за небезопасных указателей, так как Swift 2 / Xcode обновляет там синтаксисы, мы должны следовать им. Я обновил код для того же. ты;)
Виджай Авхад
Если вы подключены к Wi-Fi, но не можете подключиться к Интернету (откройте страницу Google), он все равно возвращает true для достижимого. Это не правильно.
Арильдо Младший
0

Замена Reachability от Apple переписана в Swift с замыканиями , вдохновленными tonymillion: https://github.com/ashleymills/Reachability.swift

  1. Перетащите файл Reachability.swiftв ваш проект. В качестве альтернативы используйте CocoaPods или Carthage - см. Раздел « Установка » README проекта.

  2. Получать уведомления о подключении к сети:

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

    и для остановки уведомлений

    reachability.stopNotifier()
Пьер Ф
источник
0

Alamofire

Если вы уже используете Alamofire для всех RESTful Api, вот что вы можете извлечь из этого.

Вы можете добавить следующий класс в свое приложение и вызвать, MNNetworkUtils.main.isConnected()чтобы получить логическое значение, независимо от того, подключено оно или нет.

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

Это синглтон-класс. Каждый раз, когда пользователь подключается или отключается от сети, он self.reachableкорректно переопределяет значение true / false, потому что мы начинаем прослушивать NetworkReachabilityManagerинициализацию on singleton.

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

nuynait
источник