Как узнать, включены или отключены «Данные мобильной сети» (даже при подключении через WiFi) в iOS?

У меня есть приложение, которое я хочу использовать для получения отчета о состоянии соединения через определенный интервал времени. Даже когда я подключен или связан с сетью Wi-Fi, я хотел бы знать, разрешен ли доступ к данным через сотовую сеть. Это означает, что если после проверки я могу изящно отключиться от сети Wi-Fi, зная, что есть доступное сотовое соединение, к которому будет подключено устройство.

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

Поиск аналогичного решения, доступного в Android, как описано в этом ссылка.

РАЗЪЯСНЕНИЕ

Я НЕ смотрю, есть ли у моего устройства возможности сотовой связи. Я пытаюсь установить, включил ли пользователь доступ к данным через мобильную сеть или нет, и хотел бы знать эту информацию, даже если я подключен к Wi-Fi. Пользователь может включать и выключать это, перейдя в настройки.


person Shane D    schedule 23.03.2017    source источник
comment
Привет! Вы нашли решение? У меня сейчас такая же проблема.   -  person birdy    schedule 26.03.2018
comment
Неа. Нет API как такового, который сообщит вам о наличии сотовых данных на вашем устройстве. Вы можете проверить, разрешил ли пользователь доступ к сотовым данным для вашего приложения. Но это все. Если только устройство не подключено к сотовой сети, а затем, если вы попытаетесь переключиться на какой-либо другой интерфейс (первичный интерфейс — сотовый), узнать это невозможно.   -  person Shane D    schedule 08.05.2018


Ответы (7)


Нет доступных API, с помощью которых приложение может запрашивать, включены ли мобильные данные. Вы можете использовать CTCellularData cellularDataRestrictionDidUpdateNotifier и limitedState, чтобы узнать, включил или отключил ли пользователь доступ к сотовым данным для вашего приложения. Это максимальное количество iOS, которое позволяет приложение. И даже это ненадежно, так как если вы удалите сим-карту из устройства, она все равно даст вам прежний статус ограниченного состояния.

person user2428552    schedule 23.03.2017
comment
да. Это правда. Также проверил этот API, но все равно он не дает точного результата. Если предыдущее состояние было неограниченным, если сим-карта удалена или сотовые данные отключены, оно все равно будет давать без ограничений. - person Shane D; 23.03.2017

Если вы ориентируетесь на iOS 12 или более позднюю версию, Apple представила (как часть платформы Network) класс NWPathMonitor. Вы можете (как и я) создать два разных монитора, один для сотовой связи, а другой для Wi-Fi:

let wifiMonitor = NWPathMonitor(requiredInterfaceType: .wifi)
let cellularMonitor = NWPathMonitor(requiredInterfaceType: .cellular)

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

wifiMonitor.pathUpdateHandler = { path in
    self.isWifiConnected = path.status == .satisfied                
}
cellularMonitor.pathUpdateHandler = { path in 
    self.isCellularConnected = path.status == .satisfied
}

а потом веди себя как хочешь. Не забудьте запустить мониторы, вызвав start(_:) и предоставив последовательные очереди (я предоставил две отдельные очереди); есть причуда, так что, как только вы отмените экземпляр монитора, вы должны создать новый экземпляр, потому что он не может быть перезапущен.

person Alessandro Martin    schedule 15.06.2020

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

  • Пользователь мог отключить передачу данных через своего оператора сотовой связи, поэтому, если WiFi выйдет из строя, сотовое соединение все равно не будет передавать данные.
  • Либо пользователь мог сразу изменить это в настройках.
  • Либо сотовая связь может прерваться в тот же момент, когда отключается WiFi (при входе в экранированное помещение или что-то в этом роде).
  • И многое другое может произойти тем временем в секретной комнате™.

Вам просто придется иметь дело с отключением и обеспечить доброжелательное поведение в таком случае (вместо, например, сбоя).

person Andreas Oetjen    schedule 23.03.2017
comment
Согласен с тем, что вы сказали. Причина, по которой я ищу это, заключается в том, что я хотел бы переключиться на сотовую связь, если текущее сетевое соединение Wi-Fi плохое или низкий уровень RSSI. В этом случае я бы спросил, доступна ли сотовая связь, и если да, отключился бы от сети. Ответственность за то, происходит ли после соединения передача данных по сотовой связи или нет, зависит от пользователя. На самом деле это требование, и пользователь предполагает, что на устройстве включена сотовая связь. - person Shane D; 23.03.2017
comment
Что ж, я почти уверен, что вам не разрешат (из-за ограничений Apple/отсутствия API) принудительно отключить устройство от WiFi и переключиться на сотовую сеть. Хотя в настройках устройства есть такая опция, но не знаю, можете ли вы запросить, включена она или нет. - person Andreas Oetjen; 23.03.2017
comment
Доступны API, которые позволят вам отключиться от подключенной сети и избежать подключения к определенной сети Wi-Fi, если вы хотите, и API являются общедоступными. Вот почему я просил эту другую часть, чтобы я мог завершить полный круг. Предполагая, что, поскольку я могу сделать это с сетью Wi-Fi, могу ли я сделать это аналогичным образом с сотовым интерфейсом - person Shane D; 23.03.2017
comment
Я не знаю таких API, как и другие: stackoverflow.com/questions/10231735/ - но если бы вы нашли такие API, я был бы рад, если вы могли бы показать их мне! - person Andreas Oetjen; 23.03.2017
comment
Я не говорю, что могу включать/выключать Wi-Fi программно. Я имею в виду, что доступны API, которые позволят вам отключиться от определенной сети Wi-Fi (то есть это позволит вам отключиться от сети), но устройство будет продолжать пытаться подключиться к любой другой сети, которая находится в пределах досягаемости и которая был ранее подключен, и здесь вы можете избегать подключения к сети Wi-Fi столько, сколько хотите, если хотите оставаться на связи с сотовой сетью. Обратитесь к этому, если вы хотите выполнить вышеуказанную операцию. developer.apple.com/reference/networkextension/nehotspothelper - person Shane D; 23.03.2017

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

extension SystemDataUsage {
    public static var wwanCompelete: UInt64 {
        return SystemDataUsage.getDataUsage().wirelessWanDataSent + SystemDataUsage.getDataUsage().wirelessWanDataReceived
    }
}

Затем класс:

class SystemDataUsage {

    private static let wwanInterfacePrefix = "pdp_ip"

    class func getDataUsage() -> DataUsageInfo {
        var ifaddr: UnsafeMutablePointer<ifaddrs>?
        var dataUsageInfo = DataUsageInfo()

        guard getifaddrs(&ifaddr) == 0 else { return dataUsageInfo }
        while let addr = ifaddr {
            guard let info = getDataUsageInfo(from: addr) else {
                ifaddr = addr.pointee.ifa_next
                continue
            }
            dataUsageInfo.updateInfoByAdding(info)
            ifaddr = addr.pointee.ifa_next
        }

        freeifaddrs(ifaddr)

        return dataUsageInfo
    }

    private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
        let pointer = infoPointer
        let name: String! = String(cString: pointer.pointee.ifa_name)
        let addr = pointer.pointee.ifa_addr.pointee
        guard addr.sa_family == UInt8(AF_LINK) else { return nil }

        return dataUsageInfo(from: pointer, name: name)
    }

    private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
        var networkData: UnsafeMutablePointer<if_data>?
        var dataUsageInfo = DataUsageInfo()

        if name.hasPrefix(wwanInterfacePrefix) {
            networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
            if let data = networkData {
                dataUsageInfo.wirelessWanDataSent += UInt64(data.pointee.ifi_obytes)
                dataUsageInfo.wirelessWanDataReceived += UInt64(data.pointee.ifi_ibytes)
            }
        }
        return dataUsageInfo
    }
}

Последняя структура:

struct DataUsageInfo {
    var wirelessWanDataReceived: UInt64 = 0
    var wirelessWanDataSent: UInt64 = 0

    mutating func updateInfoByAdding(_ info: DataUsageInfo) {
        wirelessWanDataSent += info.wirelessWanDataSent
        wirelessWanDataReceived += info.wirelessWanDataReceived
    }
}

Я использовал код этого ответа, вы тоже можете его проверить: Отслеживание использования мобильных данных с помощью swift

person wazowski    schedule 06.11.2020

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

//  MobileDataPolicy.h
#ifndef MobileDataPolicy_h
#define MobileDataPolicy_h

#import <Foundation/Foundation.h>
@import CoreTelephony;

NS_ASSUME_NONNULL_BEGIN

extern NSNotificationName const kMobileDataPolicyNotification;
extern NSString * const kMobileDataPolicyAllowedKey;

@interface MobileDataPolicy : NSObject
+(BOOL)isAllowed;
@end

NS_ASSUME_NONNULL_END

#endif

и

//  MobileDataPolicy.m
#import "MobileDataPolicy.h"

NSNotificationName const kMobileDataPolicyNotification = @"kMobileDataPolicyNotification";
NSString * const kMobileDataPolicyAllowedKey = @"kMobileDataPolicyAllowedKey";

@interface MobileDataPolicy ()
@property (nonatomic, readwrite) BOOL cellularDataAllowed;
@end

@implementation MobileDataPolicy

+(instancetype)singleton {
    static MobileDataPolicy * singletonInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if ( !singletonInstance ) {
            singletonInstance = [[MobileDataPolicy alloc] initReal];
            if (singletonInstance) {
                [singletonInstance setupCellularDataPolicyHandler];
            }
        }
    });
    return singletonInstance;
}

_Pragma("clang diagnostic push")
_Pragma("clang diagnostic ignored \"-Wobjc-designated-initializers\"")
-(instancetype)init {
    NSAssert(NO, @"init is not the designated initializer for instances of MobileDataPolicy. use [MobileDataPolicy isAllowed]");
    return nil;
}
_Pragma("clang diagnostic pop")

-(instancetype)initReal {
    if (!(self=[super init])) return nil;
    _cellularDataAllowed = NO;
    return self;
}

// ask for policy with [MobileDataPolicy allowed]
+(BOOL)isAllowed {
    //we need only one handler per App.
    return [MobileDataPolicy singleton].cellularDataAllowed;
}

#pragma mark setup - Cellular Data Policy Handler
- (void)setupCellularDataPolicyHandler {
    if (@available(iOS 9.0, *)) {
        CTCellularData *cellularData = [[CTCellularData alloc] init];
        
        //following handler block will run on default priority global dispatch queue
        [cellularData setCellularDataRestrictionDidUpdateNotifier:^(CTCellularDataRestrictedState state) {
            switch (state) {
                case kCTCellularDataRestrictedStateUnknown: self->_cellularDataAllowed = NO;
                    break;
                case kCTCellularDataRestricted: self->_cellularDataAllowed = NO;
                    break;
                case kCTCellularDataNotRestricted: self->_cellularDataAllowed = YES;
                    break;
                default:
                    break;
            }
            [[NSNotificationCenter defaultCenter] postNotificationName:kMobileDataPolicyNotification object:nil userInfo:@{kMobileDataPolicyAllowedKey:@(state)}];
        }];
    }
}
@end

использовать в примере как..

 //class method that inits singleton and returns state
 BOOL reachCellularData = MobileDataPolicy.isAllowed;
 NSLog(@"initial cellular data allowed for app = %@",reachCellularData ? @"YES" : @"NO");
 
 //start observing
 id<NSObject> noteKeeper = [[NSNotificationCenter defaultCenter] addObserverForName:kMobileDataPolicyNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification * _Nonnull note) {
     // do something on cellular Data Policy State change..
     int cellularDataState = [note.userInfo[kMobileDataPolicyAllowedKey] intValue];
 }];
 
 //stop observing, possibly in -(void)dealloc
 [[NSNotificationCenter defaultCenter] removeObserver:noteKeeper];
person Ol Sen    schedule 08.11.2020

https://github.com/ashleymills/Reachability.swift Reachability имеет метод определения сеть доступна через WWAN

var isReachableViaWWAN: Bool {
    // Check we're not on the simulator, we're REACHABLE and check we're on WWAN
    return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet
}
person DraganescuValentin    schedule 23.03.2017
comment
Как я уже упоминал, я не смотрю, подключен ли я в данный момент к определенному интерфейсу. Я ищу что-то, что сообщит мне, доступно ли и включено ли подключение к сотовой сети, когда я в данный момент подключен через Wi-Fi, так что в тот момент, когда я отключусь от сети Wi-Fi, я буду знать, что устройство переключится на сотовый интерфейс, в случай, когда ОС не может подключиться к определенной сети. - person Shane D; 23.03.2017

Вы можете знать все сценарии:

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNetworkChange:) name:kReachabilityChangedNotification object:nil];
        Reachability *reachablity=[Reachability reachabilityWithHostName:@"google.com"];

        [reachablity startNotifier];

        //    reachablity = [Reachability reachabilityForInternetConnection];

        NetworkStatus remoteHostStatus = [reachablity currentReachabilityStatus];
        if(remoteHostStatus == NotReachable) {
            NSLog(@"network not available ");


        }
        else if (remoteHostStatus == ReachableViaWiFi) {
            NSLog(@"connect with wifi");


        }
        else if (remoteHostStatus == ReachableViaWWAN) {
            NSLog(@"Cellulor network ");


        }
        return netwrokCheck;
person NAVEEN KUMAR    schedule 23.03.2017
comment
Как я уже упоминал, я не смотрю, подключен ли я в данный момент к определенному интерфейсу. Я ищу что-то, что сообщит мне, доступно ли и включено ли подключение к сотовой сети, когда я в данный момент подключен через Wi-Fi, так что в тот момент, когда я отключусь от сети Wi-Fi, я буду знать, что устройство переключится на сотовый интерфейс, в случай, когда ОС не может подключиться к определенной сети. - person Shane D; 23.03.2017
comment
@Shane Хотите ли вы знать о постоянном обновлении вашего приложения. Когда ваши мобильные данные переключаются с мобильного на Wi-Fi и с Wi-Fi на мобильные данные. - person NAVEEN KUMAR; 23.03.2017
comment
Я не хочу знать о состоянии моего текущего интерфейса после переключения. Уведомление или какой-либо обратный вызов также будут работать. Но то, что я ищу, это информация о текущем состоянии сотовой связи (доступно ‹доступна ли сим-карта в устройстве (надеюсь, данные доступны)› и включено‹в настройках Мобильные данные включены›), даже когда я подключен к сети Wi-Fi. Причина, по которой я ищу это, заключается в том, что я хотел бы переключиться на сотовую связь, если текущее сетевое соединение Wi-Fi плохое. - person Shane D; 23.03.2017
comment
CTTelephonyNetworkInfo *network_Info = [CTTelephonyNetworkInfo новый]; CTCarrier *carrier = network_Info.subscriberCellularProvider; с помощью этого вы можете узнать, доступен сим или нет, а с помощью класса достижимости вы можете узнать типы сотовых данных. и средства доступа к мобильным данным (включено/выключено). если вы хотите переключиться -- (Wi-Fi в подвал, вам нужно очистить данные в подвале сильнее, чем Wi-Fi) вам нужно знать, включена ли SIM-карта 3g или 4g или 2g, вы можете найти, используя класс доступности. если вы хотите, я буду это тоже - person NAVEEN KUMAR; 23.03.2017
comment
У меня есть API, который скажет мне, поддерживает ли устройство сим-карту, и тогда вы правы, что от абонента я получу имя и получу уверенность в том, что сим-карта доступна. Но я не уверен в этой части, что класс достижимости u может знать типы сотовых данных. и средства доступа к мобильным данным (включено/выключено). если вы хотите переключиться -- (Wi-Fi в подвал, вам нужно очистить подвал, данные сильнее, чем Wi-Fi) ... . Будут ли курсы доступности сообщать мне, включена ли у меня мобильная передача данных? Потому что я копал модуль доступности слева и справа. - person Shane D; 23.03.2017
comment
Если вы можете использовать вышеуказанные функции (зная, что на устройстве включена сотовая связь), это будет очень полезно. Независимо от того, включен ли сим с LTE или 2G, я этого не хочу. Кроме того, я даже не хочу, если в настройках включены сотовые данные, но передача данных или нет подключения. Достаточно только определить, включены ли мобильные данные. - person Shane D; 23.03.2017
comment
для этого NetworkStatus remoteHostStatus = [reachablity currentReachabilityStatus]; если он равен мобильным данным ReachableViaWWAN, включенным пользователем. в противном случае нет remoteHostStatus == ReachableViaWWAN. если его статус равен ReachableViaWiFi, он подключен к Wi-Fi remoteHostStatus == ReachableViaWiFi. Я надеюсь, что это решит вашу проблему - person NAVEEN KUMAR; 23.03.2017
comment
Давайте продолжим обсуждение в чате. - person Shane D; 23.03.2017
comment
Ваше решение не решает дело. Я думаю, вы не в состоянии понять вопрос. Ваш ответ сработает только тогда, когда я полностью перейду на интерфейс. В самом API вашего ответа указано, что currentReachabilityStatus . это даст вам текущий статус. Это означает, что если вы подключены к Wi-Fi, он даст вам через Wi-Fi и аналогично для сотовой связи, которая будет отображаться как wwan. - person Shane D; 23.03.2017