【问题标题】:How to tell if 'Mobile Network Data' is enabled or disabled (even when connected by WiFi) in iOS?如何判断 iOS 中是否启用或禁用了“移动网络数据”(即使通过 WiFi 连接)?
【发布时间】:2017-08-15 15:33:24
【问题描述】:

我有一个应用程序,我希望能够在某个特定时间间隔后使用它来获取连接状态报告。 即使我已连接或关联到 Wifi 网络,我也想知道是否通过蜂窝网络启用了数据访问。这意味着,如果在检查之后,我可以优雅地解除与 wifi 网络的关联,因为我知道设备将连接到可用的蜂窝连接。

当前的可达性方法只会在连接到蜂窝网络时才向我提供有关蜂窝网络可用性的信息,并且在实际连接到接口之前没有太多关于获取此数据的信息。

link 中所述,正在寻找与 android 中可用的类似解决方案。

澄清

我不想查看我的设备是否具有蜂窝网络功能。我正在尝试确定用户是否已启用/禁用通过移动网络访问数据,并且即使我已连接到 Wifi,也想知道此信息。用户可以通过转到“设置”来打开和关闭此功能。

【问题讨论】:

  • 嗨!你找到解决方案了吗?我现在也有同样的问题。
  • 不。没有这样的 api 可以告诉您设备中蜂窝数据的可用性。您可以检查用户是否为您的应用程序启用了蜂窝数据访问。但就是这样。除非设备连接到蜂窝,然后如果您尝试切换到其他接口(主接口是蜂窝),则无法知道。

标签: ios objective-c swift networking cellular-network


【解决方案1】:

https://github.com/ashleymills/Reachability.swift 可达性有一种方法可以确定网络是否可以通过 WWAN 访问

var isReachableViaWWAN: Bool {
    // Check we're not on the simulator, we're REACHABLE and check we're on WWAN
    return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet
}

【讨论】:

  • 正如我所提到的,我不是在寻找我当前是否连接到特定接口。我正在寻找可以告诉我蜂窝网络连接是否“可用”和“启用”、“当我当前通过 wifi 连接时”的东西,这样当我与 wifi 网络解除关联时,我就会知道该设备将切换到蜂窝接口,以防操作系统无法关联到特定网络。
【解决方案2】:

你可以知道所有的场景:

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

【讨论】:

  • 正如我所提到的,我不是在寻找我当前是否连接到特定接口。我正在寻找可以告诉我蜂窝网络连接是否“可用”和“启用”、“当我当前通过 wifi 连接时”的东西,这样当我与 wifi 网络解除关联时,我就会知道该设备将切换到蜂窝接口,以防操作系统无法关联到特定网络。
  • @Shane 你想知道你的应用程序的持续更新吗?当你的移动数据切换到移动到 wifi 和 wifi 到移动数据时。
  • 切换后不想知道当前界面的状态。通知器或某种回调也将起作用。但我正在寻找的是,有关当前蜂窝状态的信息(其可用 并启用),即使我在连接到 wifi 网络。我寻找这个的原因是,如果当前的 wifi 网络连接不好,我想切换到蜂窝网络。
  • CTTelephonyNetworkInfo *network_Info = [CTTelephonyNetworkInfo new]; CTCarrier *carrier = network_Info.subscriberCellularProvider;通过使用这个你可以知道 sim 是否可用,通过使用可达性类你可以知道蜂窝数据类型。和移动数据可用方式(启用/禁用)。如果你想切换——(wifi 到地窖你需要清除地窖数据比 wifi 更强大)你需要知道 SIM 卡是否启用 3g 或 4g 或 2g 你可以使用可达性类找到。如果你想我会fw那也是
  • 您的解决方案无法解决问题。我猜你无法理解这个问题。只有当我完全切换到界面时,您的答案才会起作用。您的答案的 api 本身指出 "currentReachabilityStatus" 。它将为您提供当前状态这意味着,如果您连接到 wifi,它将通过 wifi 为您提供,类似地,对于蜂窝网络,它将提供为 wwan。
【解决方案3】:

我认为你必须重新考虑你的计划:你永远无法确定可靠的(数据)连接是否会在未来的任何时间点可用:

  • 用户可能已禁用通过其蜂窝提供商的数据传输,因此如果 WiFi 出现故障,蜂窝连接仍然不会提供数据。
  • 或者用户可以立即在设置中进行更改。
  • 或者蜂窝连接可能会在 WiFi 断开的同一时刻断开(进入屏蔽室等)
  • 还有更多的事情可能发生,同时在一个密室™

在这种情况下,您只需处理断开连接并提供仁慈的行为(而不是例如崩溃)。

【讨论】:

  • 同意你说的。我寻找这个的原因是,如果当前的 wifi 网络连接不好或 RSSI 低,我想切换到蜂窝网络。在这种情况下,我会询问蜂窝是否可用,如果是,将与网络解除关联。无论是在连接后,蜂窝数据传输是否发生,还是蜂窝连接中断,都由用户负责。这实际上是一项要求,用户假设设备中启用了蜂窝连接。
  • 好吧,我很确定您不会被允许(由于 Apple 限制/缺少 API)强制设备从 WiFi 断开连接并切换到蜂窝网络。设备的设置里面虽然有这样一个选项,但是不知道能不能查询到是否开启。
  • 有可用的 API,如果您愿意,并且 API 是公开的,可以让您与已连接的网络解除关联并避免连接到特定的 wifi 网络。这就是为什么我要求这部分,以便我可以完成整个循环。假设既然我可以用 wifi 网络做到这一点,我是否可以用蜂窝接口类似地做到这一点
  • 我不知道任何这样的 API,其他人也不知道:stackoverflow.com/questions/10231735/… - 但如果你找到了这样的 API,如果你能告诉我,我会很高兴!
  • 我并不是说我可以通过编程方式打开/关闭 wifi。我所说的是可用的 api,它可以让您断开与特定“wifi”网络的连接(也就是说,它将允许您与网络解除关联),但设备将继续尝试关联到范围内的任何其他网络并且之前已连接,如果您想保持与蜂窝网络的连接,您可以在此处避免连接到 wifi 网络。如果要进行上述操作,请参阅此。 developer.apple.com/reference/networkextension/nehotspothelper
【解决方案4】:

没有可用的 api 可供应用查询是否启用了移动数据。您可以使用 CTCellularData 的cellularDataRestrictionDidUpdateNotifier 和restrictedState 来了解用户是否为您的应用程序启用或禁用了蜂窝数据访问。这是 iOS 允许应用程序的最大值。即使这也不可靠,因为如果您从设备中移除 sim,它仍然会给您早期的受限状态状态。

【讨论】:

  • 是的。这是真实的。也检查了这个 api,但它仍然没有给出准确的结果。如果之前的状态是无限制的,如果sim被移除或蜂窝数据被禁用,它仍然会给予无限制。
【解决方案5】:

如果您的目标是 iOS 12 或更高版本,Apple 已引入(作为Network 框架的一部分)NWPathMonitor 类。 您可以(像我一样)实例化两个不同的监视器,一个用于蜂窝网络,另一个用于 wifi:

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(_:) 并提供串行队列来启动监视器(我提供了两个单独的队列); 有个怪癖,一旦你取消了一个监视器实例,你必须实例化一个新的实例,因为它无法重新启动。

【讨论】:

    【解决方案6】:

    前段时间我试图弄清楚如何解决这个问题,我很遗憾地说没有 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
        }
    }
    

    我使用了这个答案的代码,你也可以检查一下: Track cellular data usage using swift

    【讨论】:

      【解决方案7】:

      我的解决方案看起来“有点”有点矫枉过正,也许有人觉得它有用。 但这个想法是将用户蜂窝数据应用程序设置考虑在内。 它使用单例来避免双重启动,并保持一个 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];
      

      【讨论】:

        猜你喜欢
        • 2012-09-30
        • 2010-11-19
        • 1970-01-01
        • 2020-12-09
        • 2014-08-19
        • 1970-01-01
        • 2011-04-24
        • 2015-07-02
        • 2014-02-11
        相关资源
        最近更新 更多