Reputation: 884
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.
Upvotes: 12
Views: 4432
Reputation: 3348
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];
Upvotes: 0
Reputation: 1526
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
Upvotes: 0
Reputation: 898
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.
Upvotes: 7
Reputation: 81
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.
Upvotes: 6
Reputation: 10199
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:
You'll simply have to deal with disconnectivity and provide a benevolent behavoir in such a case (instead of e.g. crashing).
Upvotes: 0
Reputation: 667
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;
Upvotes: -2
Reputation: 872
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
}
Upvotes: -1