如何在 iOS 可达性中检测网络信号强度 [英] How to detect Network Signal Strength in iOS Reachability
问题描述
我正在 iOS 中创建一个新的旅行应用程序,该应用程序高度依赖地图,将包含两个地图.
<块引用>- 当用户拥有强大的网络信号(Apple Maps)时,我的第一个地图会起作用.
- 当它们不是任何网络或信号非常低(离线)时,将使用我的第二个地图地图框).
为什么我在一个应用程序中有两个不同的地图?我的应用程序是一个方向应用程序,因此当用户的网络非常低或没有网络时,它将转到离线地图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
设置为足够大处理成功连接的所有合理排列(即,不要过于激进地使用低值).
为此,只需确保将您的视图控制器设置为 MKMapView
的 delegate
.然后你可以这样做:
@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.
- My first Map will work when the user has a strong Network Signal (Apple Maps).
- 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屋!