如何在 iOS 可达性中检测网络信号强度 [英] How to detect Network Signal Strength in iOS Reachability

查看:34
本文介绍了如何在 iOS 可达性中检测网络信号强度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 iOS 中创建一个新的旅行应用程序,该应用程序高度依赖地图,将包含两个地图.

<块引用>

  1. 当用户拥有强大的网络信号(Apple Maps)时,我的第一个地图会起作用.
  2. 当它们不是任何网络或信号非常低(离线)时,将使用我的第二个地图地图框).

为什么我在一个应用程序中有两个不同的地图?我的应用程序是一个方向应用程序,因此当用户的网络非常低或没有网络时,它将转到离线地图MapBox.此外,Apple Maps 将集成 Yelp 而不是离线 Map MapBox.

所以我的问题是:如何检测 WiFi、4G LTE 和 3G 中的网络信号.

解决方案

我最初的想法是计算下载文件的时间,看看需要多长时间:

@interface ViewController() @property (nonatomic) CFAbsoluteTime startTime;@property (nonatomic) CFAbsoluteTime stopTime;@property (nonatomic) long long bytesReceived;@property (nonatomic, copy) void (^speedTestCompletionHandler)(CGFloat megabytesPerSecond, NSError *error);@结尾@实现视图控制器- (void)viewDidLoad {[超级viewDidLoad];[self testDownloadSpeedWithTimout:5.0 completionHandler:^(CGFloat megabytesPerSecond, NSError *error) {NSLog(@"%0.1f; error = %@", megabytesPerSecond, error);}];}///测试下载速度//////通过下载一些预定的资源来测试连接的速度.或者,您可以添加///用于测试连接的 URL 作为此方法的参数.//////@param timeout 请求的最长时间.///@param completionHandler 请求完成(或超时)时要调用的块.///这个闭包的error参数表示下载是否有错误///资源(超时除外).//////@note 注意,超时参数不必足以下载整个///资源,而只是足够长以测量下载速度.- (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 = 超时;NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];[[会话 dataTaskWithURL:url] 简历];}- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {self.bytesReceived += [数据长度];self.stopTime = CFAbsoluteTimeGetCurrent();}- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {CFAbsoluteTime elapsed = self.stopTime - self.startTime;CGFloat 速度 = 已过 != 0 ?self.bytesReceived/(CFAbsoluteTimeGetCurrent() - self.startTime)/1024.0/1024.0 : -1;//将超时视为没有错误(因为我们正在测试速度,不担心我们是否获得了整个资源if (error == nil || ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorTimedOut)) {self.speedTestCompletionHandler(speed, nil);} 别的 {self.speedTestCompletionHandler(速度,错误);}}@结尾

注意,这衡量的速度包括启动连接的延迟.您也可以在 didReceiveResponse,如果您想考虑初始延迟.

<小时>

这样做后,回想起来,我不喜欢花时间或带宽下载对应用程序没有实际好处的东西.因此,作为替代方案,我可能会建议一种更实用的方法:为什么不尝试打开一个 MKMapView 并查看完成下载地图需要多长时间?如果失败或花费的时间超过一定时间,请切换到离线地图.同样,这里有相当多的可变性(不仅因为网络带宽和延迟,还因为一些地图图像似乎已被缓存),因此请确保将 kMaximumElapsedTime 设置为足够大处理成功连接的所有合理排列(即,不要过于激进地使用低值).

为此,只需确保将您的视图控制器设置为 MKMapViewdelegate.然后你可以这样做:

@interface ViewController() @property (nonatomic, strong) NSDate *startDate;@结尾静态 CGFloat const kMaximumElapsedTime = 5.0;@实现视图控制器//在此处插入其余的实现#pragma mark - MKMapViewDelegate 方法- (void)mapViewWillStartLoadingMap:(MKMapView *)mapView {NSDate *localStartDate = [NSDate 日期];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){//检查是否://(a) 开始日期属性不为零(因为如果是,我们//地图下载完成);和//(b) 开始日期属性与我们设置的值相同//上面,因为这个地图下载可能已经完成,但是//我们已经在下载下一个//      地图.if (self.startDate && self.startDate == localStartDate){[[[UIAlertView alloc] initWithTitle:nil消息:[NSString stringWithFormat:@"%.1f 后映射超时", delayInSeconds]代表:无取消按钮标题:@"好的"otherButtonTitles:nil] 显示];}});}- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error {self.startDate = nil;[[[UIAlertView alloc] initWithTitle:nilmessage:@"在线地图失败"代表:无取消按钮标题:@"好的"otherButtonTitles:nil] 显示];}- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView{NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceDate:self.startDate];self.startDate = nil;self.statusLabel.text = [NSString stringWithFormat:@"%.1f seconds", elapsed];}

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.

解决方案

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

这篇关于如何在 iOS 可达性中检测网络信号强度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆