Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect Network Signal Strength in iOS Reachability

I am creating a new Traveling Application in iOS, this application is highly dependent on Maps and will include two Maps.

  1. My first Map will work when the user has a strong Network Signal (Apple Maps).
  2. My second Map will be used when their isn't any Network or really Low signal (Offline MapBox).

Why do I have two different maps in one Application? My Application is a Direction App, so when the user has really low network or none it will go to the offline Map MapBox. Also the Apple Maps will have Yelp integration and not the offline Map MapBox.

So my Question: How can I detect the network signal in WiFi, 4G Lte, and 3G. MapBox Offline Image

like image 600
iProgrammed Avatar asked Jan 26 '13 00:01

iProgrammed


People also ask

What does 3001 12345 do to your phone?

Dial *3001#12345#* and press the Call button. This will launch the Field Test Mode app and where the bars/dots were in the top left corner of the screen, you'll now see a negative number. The negative number is the decibel signal strength reading and should be followed by the carrier name and then the network type.

How do I check network signal strength?

For Android Android users have the signal strength feature hidden deep down in Settings. Go to the Settings app > About phone > Status > SIM status > Signal Strength. You will see numbers expressed in dBm (decibel milliwatts).

How does Nwpathmonitor check Internet connection?

You can use the function usesInterfaceType(_:) to check which interface type this network path uses. This is the most effective way to figure out if your app is connected over WiFi, cellular or ethernet. print(“It's WiFi!”)

How do I check WiFi signal strength on my iPhone?

Just go to your iPhone or iPad Settings app (not the app's settings), tap Airport Utility from the list, and then toggle on “Wi-Fi Scanner.” Now, go back to the Airport Utility app and start a scan. You'll see dBm measurements expressed as RSSI.


3 Answers

My original thought was to time the download of a file, and see how long it takes:

@interface ViewController () <NSURLSessionDelegate, NSURLSessionDataDelegate>

@property (nonatomic) CFAbsoluteTime startTime;
@property (nonatomic) CFAbsoluteTime stopTime;
@property (nonatomic) long long bytesReceived;
@property (nonatomic, copy) void (^speedTestCompletionHandler)(CGFloat megabytesPerSecond, NSError *error);

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self testDownloadSpeedWithTimout:5.0 completionHandler:^(CGFloat megabytesPerSecond, NSError *error) {
        NSLog(@"%0.1f; error = %@", megabytesPerSecond, error);
    }];
}

/// Test speed of download
///
/// Test the speed of a connection by downloading some predetermined resource. Alternatively, you could add the
/// URL of what to use for testing the connection as a parameter to this method.
///
/// @param timeout             The maximum amount of time for the request.
/// @param completionHandler   The block to be called when the request finishes (or times out).
///                            The error parameter to this closure indicates whether there was an error downloading
///                            the resource (other than timeout).
///
/// @note                      Note, the timeout parameter doesn't have to be enough to download the entire
///                            resource, but rather just sufficiently long enough to measure the speed of the download.

- (void)testDownloadSpeedWithTimout:(NSTimeInterval)timeout completionHandler:(nonnull void (^)(CGFloat megabytesPerSecond, NSError * _Nullable error))completionHandler {
    NSURL *url = [NSURL URLWithString:@"http://insert.your.site.here/yourfile"];

    self.startTime = CFAbsoluteTimeGetCurrent();
    self.stopTime = self.startTime;
    self.bytesReceived = 0;
    self.speedTestCompletionHandler = completionHandler;

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    configuration.timeoutIntervalForResource = timeout;
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    [[session dataTaskWithURL:url] resume];
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    self.bytesReceived += [data length];
    self.stopTime = CFAbsoluteTimeGetCurrent();
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    CFAbsoluteTime elapsed = self.stopTime - self.startTime;
    CGFloat speed = elapsed != 0 ? self.bytesReceived / (CFAbsoluteTimeGetCurrent() - self.startTime) / 1024.0 / 1024.0 : -1;

    // treat timeout as no error (as we're testing speed, not worried about whether we got entire resource or not

    if (error == nil || ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorTimedOut)) {
        self.speedTestCompletionHandler(speed, nil);
    } else {
        self.speedTestCompletionHandler(speed, error);
    }
}

@end

Note, this measures the speed including the latency of starting the connection. You could alternatively initialize startTime in didReceiveResponse, if you wanted to factor out that initial latency.


Having done that, in retrospect, I don't like spending time or bandwidth downloading something that has no practical benefit to the app. So, as an alternative, I might suggest a far more pragmatic approach: Why don't you just try to open a MKMapView and see how long it takes to finish downloading the map? If it fails or if it takes more than a certain amount of time, then switch to your offline map. Again, there is quite a bit of variability here (not only because network bandwidth and latency, but also because some map images appear to be cached), so make sure to set a kMaximumElapsedTime to be large enough to handle all the reasonable permutations of a successful connection (i.e., don't be too aggressive in using a low value).

To do this, just make sure to set your view controller to be the delegate of the MKMapView. And then you can do:

@interface ViewController () <MKMapViewDelegate>
@property (nonatomic, strong) NSDate *startDate;
@end

static CGFloat const kMaximumElapsedTime = 5.0;

@implementation ViewController

// insert the rest of your implementation here

#pragma mark - MKMapViewDelegate methods

- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView {
    NSDate *localStartDate = [NSDate date];
    self.startDate = localStartDate;

    double delayInSeconds = kMaximumElapsedTime;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        // Check to see if either:
        //   (a) start date property is not nil (because if it is, we 
        //       finished map download); and
        //   (b) start date property is the same as the value we set
        //       above, as it's possible this map download is done, but
        //       we're already in the process of downloading the next
        //       map.

        if (self.startDate && self.startDate == localStartDate)
        {
            [[[UIAlertView alloc] initWithTitle:nil
                                        message:[NSString stringWithFormat:@"Map timed out after %.1f", delayInSeconds]
                                       delegate:nil
                              cancelButtonTitle:@"OK"
                              otherButtonTitles:nil] show];
        }
    });
}

- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error {
    self.startDate = nil;

    [[[UIAlertView alloc] initWithTitle:nil
                                message:@"Online map failed"
                               delegate:nil
                      cancelButtonTitle:@"OK"
                      otherButtonTitles:nil] show];
}

- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView
{
    NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:self.startDate];
    self.startDate = nil;
    self.statusLabel.text = [NSString stringWithFormat:@"%.1f seconds", elapsed];
}
like image 130
Rob Avatar answered Oct 01 '22 11:10

Rob


For Swift

class NetworkSpeedProvider: NSObject {

var startTime = CFAbsoluteTime()
var stopTime = CFAbsoluteTime()
var bytesReceived: CGFloat = 0
var speedTestCompletionHandler: ((_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void)? = nil

func test() {

    testDownloadSpeed(withTimout: 5.0, completionHandler: {(_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void in
        print("%0.1f; error = \(megabytesPerSecond)")
    })
  }
}


extension NetworkSpeedProvider: URLSessionDataDelegate, URLSessionDelegate {


func testDownloadSpeed(withTimout timeout: TimeInterval, completionHandler: @escaping (_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void) {



    // you set any relevant string with any file
    let urlForSpeedTest = URL(string: "https://any.jpg")




    startTime = CFAbsoluteTimeGetCurrent()
    stopTime = startTime
    bytesReceived = 0
    speedTestCompletionHandler = completionHandler
    let configuration = URLSessionConfiguration.ephemeral
    configuration.timeoutIntervalForResource = timeout
    let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)

    guard let checkedUrl = urlForSpeedTest else { return }

    session.dataTask(with: checkedUrl).resume()
}

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    bytesReceived += CGFloat(data.count)
    stopTime = CFAbsoluteTimeGetCurrent()
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    let elapsed = (stopTime - startTime) //as? CFAbsoluteTime
    let speed: CGFloat = elapsed != 0 ? bytesReceived / (CGFloat(CFAbsoluteTimeGetCurrent() - startTime)) / 1024.0 / 1024.0 : -1.0
    // treat timeout as no error (as we're testing speed, not worried about whether we got entire resource or not
    if error == nil || ((((error as NSError?)?.domain) == NSURLErrorDomain) && (error as NSError?)?.code == NSURLErrorTimedOut) {
        speedTestCompletionHandler?(speed, nil)
    }
    else {
        speedTestCompletionHandler?(speed, error)
    }
  }
}
like image 31
Leonif Avatar answered Oct 01 '22 13:10

Leonif


I believe a google search will help.

Look out for the following thread on StackOverflow—

iOS wifi scan, signal strength

iPhone signal strength

So, I don't think you can still do this without using private APIs.

like image 27
p0lAris Avatar answered Oct 01 '22 11:10

p0lAris