web-dev-qa-db-ja.com

iOSの到達可能性でネットワーク信号強度を検出する方法

私はiOSで新しいトラベルアプリケーションを作成しています。このアプリケーションはマップに大きく依存しており、2つのマップが含まれます。

  1. 私の最初のマップは、ユーザーが強いネットワーク信号(Appleマップ)を持っているときに機能します。
  2. 私の2番目のマップは、ネットワークまたは実際に低信号(オフラインMapBox)でない場合に使用されます。

1つのアプリケーションに2つの異なるマップがあるのはなぜですか?私のアプリケーションは方向アプリケーションなので、ユーザーのネットワークが非常に低いかまったくない場合、オフラインマップMapBoxに移動します。また、Appleマップには、オフラインマップMapBoxではなく、Yelp統合が含まれます。

だから私の質問:WiFi、4G Lte、3Gでネットワーク信号をどのように検出できますか? MapBox Offline Image

15
iProgrammed

私の当初の考えは、ファイルのダウンロードの時間を計り、それがどれだけかかるかを見ることでした:

@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

これは、接続開始の遅延を含む速度を測定することに注意してください。または、初期レイテンシを除外する場合は、startTimedidReceiveResponse で初期化することもできます。


そうした後、振り返ってみると、アプリに実際的なメリットがない何かをダウンロードするのに時間や帯域幅を費やすことは好きではありません。したがって、代替手段として、はるかに実用的なアプローチを提案できます。MKMapViewを開いて、マップのダウンロードが完了するまでにかかる時間を確認してみませんか?失敗する場合、または一定時間以上かかる場合は、オフラインマップに切り替えてください。繰り返しますが、ここにはかなりのばらつきがあります(ネットワークの帯域幅とレイテンシだけでなく、一部のマップ画像がキャッシュされているように見えるため)、kMaximumElapsedTimeをすべての処理に十分な大きさに設定してください成功した接続の合理的な順列(つまり、低い値を使用することにあまり積極的ではありません)。

これを行うには、ビューコントローラをdelegateMKMapViewに設定してください。そして、あなたは行うことができます:

@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];
}
39
Rob

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)
    }
  }
}
1
Leonif

グーグル検索が役立つと思います。

StackOverflowで次のスレッドを探します—

iOS wifiスキャン、信号強度

iPhoneの信号強度

したがって、プライベートAPIを使用せずにこれを実行できるとは思いません。

1
p0lAris