web-dev-qa-db-ger.com

So ermitteln Sie die Netzwerksignalstärke in iOS-Erreichbarkeit

Ich erstelle eine neue Reiseanwendung in iOS. Diese Anwendung hängt stark von Maps ab und enthält zwei Maps.

  1. Meine erste Map funktioniert, wenn der Benutzer ein starkes Netzwerksignal (Apple Maps) hat.
  2. Meine zweite Map wird verwendet, wenn es kein Netzwerk- oder wirklich Low-Signal gibt (Offline MapBox).

Warum habe ich zwei verschiedene Karten in einer Anwendung? Meine Anwendung ist eine Richtungs-App. Wenn der Benutzer ein wirklich schwaches Netzwerk hat oder keines hat, wird die Offline-Map MapBox aufgerufen. Auch die Apple Maps haben die Yelp-Integration und nicht die Offline-Map MapBox

Also meine Frage: Wie kann ich das Netzwerksignal in WiFi, 4G Lte und 3G erkennen? MapBox Offline Image

14
iProgrammed

Mein ursprünglicher Gedanke war, den Download einer Datei zu testen und zu sehen, wie lange es dauert:

@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

Beachten Sie, dass dies die Geschwindigkeit einschließlich der Latenz beim Starten der Verbindung misst. Sie können alternativ startTimein didReceiveResponseNAME_ initialisieren, wenn Sie die anfängliche Latenzzeit herausfalten möchten.


Im Nachhinein mag ich es nicht, Zeit oder Bandbreite für das Herunterladen von Daten auszugeben, die keinen praktischen Nutzen für die App haben. Als Alternative könnte ich einen viel pragmatischeren Ansatz vorschlagen: Warum versuchen Sie nicht, einfach einen MKMapViewzu öffnen und zu sehen, wie lange es dauert, bis die Karte vollständig heruntergeladen ist? Wenn dies fehlschlägt oder mehr als eine bestimmte Zeit dauert, wechseln Sie zu Ihrer Offline-Karte. Auch hier gibt es einiges an Variabilität (nicht nur wegen der Bandbreite und Latenz des Netzwerks, sondern auch weil einige Kartenbilder zwischengespeichert zu sein scheinen). Stellen Sie also sicher, dass ein kMaximumElapsedTimegroß genug ist, um alle vernünftigen Permutationen von a zu verarbeiten erfolgreiche Verbindung (dh seien Sie nicht zu aggressiv, wenn Sie einen niedrigen Wert verwenden). 

Stellen Sie dazu sicher, dass Sie Ihren View-Controller als delegatedes MKMapViewfestlegen. Und dann kannst du tun:

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

Ich glaube, eine Google-Suche wird helfen.

Suchen Sie nach dem folgenden Thread in StackOverflow -

iOS-WLAN-Scan, Signalstärke

iPhone Signalstärke

Ich glaube nicht, dass Sie dies immer noch tun können, ohne private APIs zu verwenden.

1
p0lAris

Für 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)
    }
  }
}
0
Leonif