12

I have an app that I want to be able to use to get a connection status report after some certain time interval. Even when I am connected or associated to a Wifi network, I would like to know if data access is enabled over cellular network. This means, if after a check, I can gracefully disassociate from the wifi network, knowing that there is an available cellular connection to which the device will get connected to.

Current Reachability methods will only give me information about the availability of cellular only when am connected to that and there is not much information about getting this data before actually connecting to the interface.

Looking for a similar solution as available in android, as explained in this link.

CLARIFICATION

I am NOT looking to see if my device is having cellular capabilities. I AM trying to establish whether or not the user has enabled / disabled Data access over mobile network, and would like to know this information even if I am connected to Wifi. User can turn this on and off by going to Settings.

Community
  • 1
  • 1
Shane D
  • 834
  • 9
  • 23
  • 1
    Hi! Did you find solution? I have the same problem now. – birdy Mar 26 '18 at 13:22
  • Nope. There is no api as such which will tell you the availability of cellular data in your device. You can check, whether user has enabled cellular data access for your application. But that's it. Unless, the device is connected to cellular, and then if you try to switch to some other interface ( primary interface is cellular ), there is no way to know it. – Shane D May 08 '18 at 02:08

7 Answers7

6

There is no available api's by which the app can query whether mobile data is enabled. You can use CTCellularData's cellularDataRestrictionDidUpdateNotifier and restrictedState to know if user has enabled or disabled cellular data access for your application. That is the max iOS allows for an application. And even this is not reliable as if you remove the sim from the device it will still give you the earlier restricted state status.

  • Yes. This is true. Have also checked this api but still it does not give the accurate result. If the previous state was unrestricted, if the sim is removed or cellular data is disabled, it will still give unrestricted. – Shane D Mar 23 '17 at 12:40
6

If you are targeting iOS 12 or later, Apple has introduced (as part of the Network framework) the NWPathMonitor class. You can (as I did) instantiate two different monitors, one for cellular and another one for wifi:

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

Supposing you have a class that keeps track of the two connection statuses with two simple Booleans, with the pathUpdateHandler properties you could do:

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

and then handle yourself as you prefer. Remember to kickoff the monitors by calling start(_:) and providing serial queues (I provided two separate queues); there's a quirk so that, once you cancel a monitor instance, you have to instantiate a new one because it cannot be restarted.

0

I think you have to rethink about whatever you are planning: You'll never ever be able to determine if a reliable (data) connection will be available at any future point in time:

  • The user could have disabled data transfer via its cellular provider, so if WiFi goes down, the cellular connection still won't provide data.
  • Or the user could immediately change this in the settings.
  • Or the cellular connection could go down in the very same moment as the WiFi is disconnected (while entering a shielded room or sth.)
  • And many more things could happen, meanwhile in a secret room™

You'll simply have to deal with disconnectivity and provide a benevolent behavoir in such a case (instead of e.g. crashing).

Andreas Oetjen
  • 9,889
  • 1
  • 24
  • 34
  • Agree with what you said. Reason why I am looking for this is, I would want to switch to cellular if the current wifi network connection is bad or RSSI is low. In this case I would query if cellular is available and if yes, would disassociate from the network. Whether after connection, cellular data transfer is happening or whether cellular connection goes not is upon user's responsibility. This is actually a requirement and user assumes that cellular conectivity is enabled in device. – Shane D Mar 23 '17 at 10:53
  • 1
    Well, I'm pretty sure you won't be allowed (due to Apple restrictions / missing API) to force the device to disconnect from WiFi and switch to cellular network. There is such an option in the device's settings though, but don't know if you are able to query if it's enabled or not. – Andreas Oetjen Mar 23 '17 at 10:59
  • Api's are available that will allow you to disassociate from a connected network and to avoid connectivity to a particular wifi network, if you wish to and the api's are public. That's why I was asking for this other part so that I can complete the full circle. Assuming that since I can do it with a wifi network, whether I can similarly do it with cellular interface – Shane D Mar 23 '17 at 11:04
  • I don't know any such APIs, and others don't either: http://stackoverflow.com/questions/10231735/is-there-a-way-to-toggle-wifi-on-and-off-programatically-in-ios - but if you found such APIs, I would be glad if you could show them to me! – Andreas Oetjen Mar 23 '17 at 11:07
  • I am not saying I can switch on/off wifi programatically. What I am saying is api's are available which will let you to disconnect from a particular "wifi" network (that is it will allow you to disassociate from a network) , but device will keep trying to associate to any other network which is in range and which was previously connected, and here is where you can avoid connecting to a wifi network as long as you want if you want to stay connected to a cellular network. Refer this if you want to do the above operation. https://developer.apple.com/reference/networkextension/nehotspothelper – Shane D Mar 23 '17 at 11:14
0

Some time ago I tried to figure out how to solve this problem, and I am sad to say that there is no API no solve this question fast and in a simple way, so I decide to make a workaround. I thought then track the whole device cellular data usage, so if I store the cellular usage in my app, I can see if user has or not active cellular data on its device. I did it like this, first the extension:

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

Then the class:

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

Last one the struct:

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

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

I used the code of this answer, you can check it too: Track cellular data usage using swift

Diego Jiménez
  • 1,398
  • 1
  • 15
  • 26
0

my solution looks a 'little' like overkill, maybe someone find it useful. But the idea is to take users cellular data app settings into account.. It makes use of a singleton to avoid doubled initiation and keeps a BOOL to rely on and posts Notification that can be observed anywhere in the app.

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

and

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

use in example like..

 //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];
Ol Sen
  • 3,163
  • 2
  • 21
  • 30
-1

https://github.com/ashleymills/Reachability.swift Reachability has a method to determine if the network is reachable via WWAN

var isReachableViaWWAN: Bool {
    // Check we're not on the simulator, we're REACHABLE and check we're on WWAN
    return isRunningOnDevice && isReachableFlagSet && isOnWWANFlagSet
}
DraganescuValentin
  • 842
  • 2
  • 10
  • 16
  • 1
    As I have mentioned, I am not looking for whether I am currently connected to a particular interface. I am looking for something which will tell me if cellular network connection is "available" and "enabled", "when I am currently connected through wifi", so that the moment I disassociate from the wifi network, i will know that the device will switch to cellular interface, in case OS fails to associate to a particular network. – Shane D Mar 23 '17 at 10:26
-2

u can know all the scenarios:

 [[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;
NAVEEN KUMAR
  • 669
  • 6
  • 25
  • 1
    As I have mentioned, I am not looking for whether I am currently connected to a particular interface. I am looking for something which will tell me if cellular network connection is "available" and "enabled", "when I am currently connected through wifi", so that the moment I disassociate from the wifi network, i will know that the device will switch to cellular interface, in case OS fails to associate to a particular network. – Shane D Mar 23 '17 at 10:26
  • @Shane Do u want to know continuous update for ur app .when ur mobile data switch to mobile to wifi and wifi to mobile data . – NAVEEN KUMAR Mar 23 '17 at 10:45
  • I don't want to know about the status of my current interface after i switch. A notifier or some kind of callback will also work. But what I am looking for is, the info about the current cellular status ( its available and enabled ), even when I am connected to a wifi network. Reason why I am looking for this is, I would want to switch to cellular if the current wifi network connection is bad. – Shane D Mar 23 '17 at 10:50
  • CTTelephonyNetworkInfo *network_Info = [CTTelephonyNetworkInfo new]; CTCarrier *carrier = network_Info.subscriberCellularProvider; by using this u can know sim is available or not and by using reachability class u can know cellular data types. and mobile data available means (enabled/disabled). if u want to switch --(wifi to cellar u need to clear cellar data is more stronger than wifi) u need to know whether SIM card is enabled 3g or 4g or 2g u can find using reachability class .if u want I will fw that too – NAVEEN KUMAR Mar 23 '17 at 11:24
  • I have an api which will tell me whether the device supports sim card, and then you are correct that from subscriber I will get the name and will get assurance that sim card is available. But am not sure from this part , "reachability class u can know cellular data types. and mobile data available means (enabled/disabled). if u want to switch --(wifi to cellar u need to clear cellar data is more stronger than wifi) ... ". Will "Reachability classes" let me know if I have mobile data enabled? Coz I have digged the reachability module left and right. – Shane D Mar 23 '17 at 11:32
  • If you are able to do the above functionality ( knowing that cellular is enabled in device ) , that will be very helpful. Whether sim is enabled with LTE or 2G, I don't want that. Also, i dont even want to if cellular data is enabled in settings but data transfer or there is no connectivity. Only able to detect whether mobile data is enabled will be enough. – Shane D Mar 23 '17 at 11:37
  • for that NetworkStatus remoteHostStatus = [reachablity currentReachabilityStatus]; if it is equal to "ReachableViaWWAN" user enabled mobile data. other wise no remoteHostStatus == ReachableViaWWAN. if it status is equal to ReachableViaWiFi its connected to wifi remoteHostStatus == ReachableViaWiFi. I hope it will solves ur issue – NAVEEN KUMAR Mar 23 '17 at 11:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/138816/discussion-between-shane-d-and-naveen-kumar). – Shane D Mar 23 '17 at 11:57
  • 1
    Your solution does not solve the case. I guess you are not able to understand the question. Your answer will work only when I have completely switched to the interface. Your answer's api itself states that "currentReachabilityStatus" . it will give you the current status That means, if you are connected to wifi, it will give you via wifi and similarly for cellular which it will give as wwan . – Shane D Mar 23 '17 at 12:04