Ich erstelle eine neue Reiseanwendung in iOS. Diese Anwendung hängt stark von Maps ab und enthält zwei Maps.
- Meine erste Map funktioniert, wenn der Benutzer ein starkes Netzwerksignal (Apple Maps) hat.
- 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?
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 startTime
in didReceiveResponse
NAME_ 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 MKMapView
zu ö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 kMaximumElapsedTime
groß 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 delegate
des MKMapView
festlegen. 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];
}
Ich glaube, eine Google-Suche wird helfen.
Suchen Sie nach dem folgenden Thread in StackOverflow -
Ich glaube nicht, dass Sie dies immer noch tun können, ohne private APIs zu verwenden.
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)
}
}
}