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.
- Ma première carte fonctionnera lorsque l'utilisateur aura un signal réseau fort (Apple Maps).
- 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.
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];
}
Je crois qu'une recherche sur Google aidera.
Cherchez le fil suivant sur StackOverflow—
Balayage wifi iOS, force du signal
Donc, je ne pense pas que vous puissiez toujours le faire sans utiliser des API privées.
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)
}
}
}