web-dev-qa-db-fra.com

Comment détecter la force du signal du réseau dans l'accessibilité iOS

Je suis en train de créer une nouvelle application itinérante dans iOS. Cette application dépend fortement de Maps et comprendra deux cartes.

  1. Ma première carte fonctionnera lorsque l'utilisateur aura un signal réseau fort (Apple Maps).
  2. Ma deuxième carte sera utilisée quand il n’ya pas de réseau ou de signal très faible (Offline MapBox).

Pourquoi ai-je deux cartes différentes dans une application? Mon application est une application Direction. Ainsi, lorsque l'utilisateur dispose d'un réseau très faible ou nul, il se connecte à la carte hors ligne MapBox. De plus, les cartes Apple auront une intégration Yelp et non la carte hors ligne MapBox

Alors ma question: comment détecter le signal du réseau en WiFi, 4G Lte et 3G. MapBox Offline Image

14
iProgrammed

Ma pensée initiale était de chronométrer le téléchargement d'un fichier et de voir combien de temps cela prend:

@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

Notez que cela mesure la vitesse, y compris la latence de démarrage de la connexion. Vous pouvez également initialiser startTime dans didReceiveResponse , si vous souhaitez factoriser cette latence initiale.


Cela fait, rétrospectivement, je n’aime pas perdre de temps à télécharger quelque chose qui n’a aucun avantage pratique pour l’application. Donc, au lieu de cela, je pourrais suggérer une approche beaucoup plus pragmatique: pourquoi ne pas simplement essayer d’ouvrir une MKMapView et voir combien de temps il faut pour terminer le téléchargement de la carte? Si cela échoue ou si cela prend plus de temps, passez à votre carte hors ligne. Là encore, la variabilité est importante (non seulement en raison de la bande passante et de la latence du réseau, mais également du fait que certaines images de carte semblent être mises en cache), assurez-vous donc de définir une variable kMaximumElapsedTime suffisante connexion réussie (c.-à-d., ne soyez pas trop agressif en utilisant une valeur faible). 

Pour ce faire, assurez-vous simplement que votre contrôleur de vue est la variable delegate de la variable MKMapView. Et alors vous pouvez faire:

@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

Je crois qu'une recherche sur Google aidera.

Cherchez le fil suivant sur StackOverflow—

Balayage wifi iOS, force du signal

Force du signal de l'iPhone

Donc, je ne pense pas que vous puissiez toujours le faire sans utiliser des API privées.

1
p0lAris

Pour 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