VKontakte-Benutzer tauschen täglich 10 Milliarden Nachrichten aus. Sie senden sich gegenseitig Fotos, Comics, Memes und andere Anhänge. Wir werden Ihnen 
URLProtocol , wie wir eine iOS-Anwendung zum Hochladen von Bildern mit 
URLProtocol , und Schritt für Schritt werden wir herausfinden, wie wir unsere eigene implementieren können.
Vor ungefähr anderthalb Jahren war die Entwicklung eines neuen Nachrichtenabschnitts in der VK-Anwendung für iOS in vollem Gange. Dies ist der erste Abschnitt, der vollständig in Swift geschrieben wurde. Es befindet sich in einem separaten Modul 
vkm (VK Messages), das nichts über das Gerät der Hauptanwendung weiß. Es kann sogar in einem separaten Projekt ausgeführt werden - die grundlegenden Funktionen zum Lesen und Senden von Nachrichten funktionieren weiterhin. In der Hauptanwendung werden Nachrichtencontroller über den entsprechenden Container View Controller hinzugefügt, um beispielsweise eine Liste von Konversationen oder Nachrichten in einer Konversation anzuzeigen.
Nachrichten sind einer der beliebtesten Bereiche der mobilen VKontakte-Anwendung. Daher ist es wichtig, dass sie wie eine Uhr funktionieren. Im 
messages kämpfen wir um jede Codezeile. Es hat uns immer sehr gut gefallen, wie ordentlich die Nachrichten in die Anwendung integriert sind, und wir bemühen uns sicherzustellen, dass alles beim Alten bleibt.
Nachdem wir den Abschnitt schrittweise mit neuen Funktionen gefüllt hatten, näherten wir uns der folgenden Aufgabe: Es war erforderlich, das Foto, das an die Nachricht angehängt ist, zuerst in einem Entwurf anzuzeigen und nach dem Senden in der allgemeinen Liste der Nachrichten zu erstellen. Wir könnten einfach ein Modul hinzufügen, um mit 
PHImageManager zu arbeiten, aber zusätzliche Bedingungen erschwerten die Aufgabe.
Bei der Auswahl eines Snapshots kann der Benutzer diesen verarbeiten: Filter anwenden, drehen, zuschneiden usw. In der VK-Anwendung wird diese Funktionalität in einer separaten 
AssetService Komponente 
AssetService . Jetzt musste man aus dem Nachrichtenprojekt lernen, mit ihm zu arbeiten.
Nun, die Aufgabe ist ganz einfach, wir werden es tun. Dies ist ungefähr die durchschnittliche Lösung, da es viele Variationen gibt. Wir nehmen das Protokoll, legen es in Nachrichten ab und füllen es mit Methoden. Wir erweitern den AssetService, passen das Protokoll an und fügen unsere Cache-Implementierung hinzu! für die Viskosität. Dann fügen wir die Implementierung in Nachrichten ein, fügen sie einem Dienst oder Manager hinzu, der mit all dem funktioniert, und beginnen, sie zu verwenden. Gleichzeitig kommt immer noch ein neuer Entwickler und verurteilt, während er versucht, alles herauszufinden, flüsternd ... (na ja, verstehen Sie). Gleichzeitig tritt Schweiß auf seiner Stirn auf.
Diese Entscheidung 
gefiel uns nicht . Es werden neue Entitäten 
AssetService , über die Nachrichtenkomponenten bei der Arbeit mit Bildern aus 
AssetService Bescheid wissen 
AssetService . Der Entwickler muss außerdem zusätzliche Arbeit leisten, um herauszufinden, wie dieses System funktioniert. Schließlich gab es eine zusätzliche implizite Verknüpfung zu den Komponenten des Hauptprojekts, die wir zu vermeiden versuchen, damit der Nachrichtenabschnitt weiterhin als unabhängiges Modul funktioniert.
Ich wollte das Problem lösen, damit das Projekt überhaupt nichts darüber wusste, welche Art von Bild ausgewählt wurde, wie es gespeichert werden sollte, ob es speziell geladen und gerendert werden musste. Darüber hinaus haben wir bereits die Möglichkeit, herkömmliche Bilder aus dem Internet herunterzuladen, nur werden sie nicht über einen zusätzlichen Dienst heruntergeladen, sondern einfach per 
URL . Tatsächlich gibt es keinen Unterschied zwischen den beiden Bildtypen. Nur einige werden lokal gespeichert, während andere auf dem Server gespeichert werden.
Wir hatten also eine sehr einfache Idee: Was ist, wenn das Laden lokaler Assets auch über eine 
URL erlernt werden 
URL ? Es scheint, als würde dies mit einem Klick auf 
Thanos ' Finger alle unsere Probleme lösen: Sie müssen nichts über 
AssetService wissen, neue Datentypen hinzufügen und die Entropie vergeblich erhöhen, lernen, einen neuen Bildtyp zu laden, sich um das Zwischenspeichern von Daten kümmern. Klingt nach einem Plan.
Wir brauchen nur eine URL
Wir haben diese Idee berücksichtigt und beschlossen, das 
URL Format zu definieren, das zum Laden lokaler Assets verwendet wird:
 asset://?id=123&width=1920&height=1280 
Wir werden den Wert der Eigenschaft 
localIdentifier von 
localIdentifier als 
PHObject und die Parameter 
width und 
height , um die Bilder der gewünschten Größe zu laden. Wir fügen auch einige weitere Parameter wie 
crop , 
filter und 
rotate , mit denen Sie mit den Informationen des verarbeiteten Bildes arbeiten können.
Um diese 
URL erstellen wir ein 
AssetURLProtocol :
 class AssetURLProtocol: URLProtocol { } 
Seine Aufgabe besteht darin, das Image über 
AssetService zu laden und die bereits gebrauchsfertigen Daten zurückzugeben.
All dies ermöglicht es uns, die Arbeit des 
URL Protokolls und des 
URL Loading System fast vollständig zu delegieren.
Innerhalb der Nachrichten kann mit den gängigsten 
URL nur in einem anderen Format gearbeitet werden. Es wird auch möglich sein, den vorhandenen Mechanismus zum Laden von Bildern wiederzuverwenden. Es ist sehr einfach, in der Datenbank zu serialisieren und das Zwischenspeichern von Daten über Standard- 
URLCache .
Hat es geklappt? Wenn Sie beim Lesen dieses Artikels ein Foto aus der Galerie an die Nachricht in der VKontakte-Anwendung anhängen können, dann ja :)
Um zu verdeutlichen, wie Sie Ihr 
URLProtocol implementieren, schlage ich vor, dies 
URLProtocol eines Beispiels zu betrachten.
Wir haben uns die Aufgabe gestellt: eine einfache Anwendung mit einer Liste zu implementieren, in der Sie eine Liste von Kartenschnappschüssen an den angegebenen Koordinaten anzeigen müssen. Zum Herunterladen von Snapshots verwenden wir den Standard- 
MKMapSnapshotter von 
MapKit und laden Daten über das benutzerdefinierte 
URLProtocol . Das Ergebnis könnte ungefähr so aussehen:
Zunächst implementieren wir den Mechanismus zum Laden von Daten per 
URL . Um den Kartenschnappschuss anzuzeigen, müssen wir die Koordinaten des Punktes kennen - seine Breite und Länge ( 
latitude , 
longitude ). Definieren Sie das benutzerdefinierte 
URL Format, mit dem Informationen geladen werden sollen:
 map://?latitude=59.935634&longitude=30.325935 
Jetzt implementieren wir 
URLProtocol , das solche Links verarbeitet und das gewünschte Ergebnis generiert. Erstellen wir die 
MapURLProtocol Klasse, die wir von der Basisklasse 
URLProtocol erben. Trotz seines Namens ist 
URLProtocol eine abstrakte Klasse. 
URLProtocol Sie nicht verlegen, hier verwenden wir andere Konzepte - 
URLProtocol repräsentiert genau das 
URL Protokoll und hat keine Beziehung zu OOP-Begriffen. Also 
MapURLProtocol :
 class MapURLProtocol: URLProtocol { } 
Jetzt definieren wir einige erforderliche Methoden neu, ohne die das 
URL Protokoll nicht funktioniert:
1. canInit(with:)
 override class func canInit(with request: URLRequest) -> Bool { return request.url?.scheme == "map" } 
Die Methode 
canInit(with:) wird benötigt, um anzugeben, welche Arten von Anforderungen unser 
URL Protokoll verarbeiten kann. Angenommen, das Protokoll verarbeitet in diesem Beispiel nur Anforderungen mit einem 
map in der 
URL . Vor dem Starten einer Anforderung durchläuft die 
URL Loading System alle für die Sitzung registrierten Protokolle und ruft diese Methode auf. Das erste registrierte Protokoll, das bei dieser Methode 
true zurückgibt, wird zur Verarbeitung der Anforderung verwendet.
canonicalRequest(for:)
 override class func canonicalRequest(for request: URLRequest) -> URLRequest { return request } 
Die Methode 
canonicalRequest(for:) soll die Anforderung auf kanonische Form reduzieren. Die Dokumentation besagt, dass die Implementierung des Protokolls selbst entscheidet, was als Definition dieses Konzepts zu betrachten ist. Hier können Sie das Schema normalisieren, der Anforderung bei Bedarf Header hinzufügen usw. Die einzige Voraussetzung für die Funktion dieser Methode ist, dass für jede eingehende Anforderung immer das gleiche Ergebnis erzielt wird, auch weil diese Methode auch zur Suche nach zwischengespeicherten Antworten verwendet wird Anfragen in 
URLCache .
3. startLoading()
Die Methode 
startLoading() beschreibt die gesamte Logik zum Laden der erforderlichen Daten. In diesem Beispiel müssen Sie die Anforderungs- 
URL analysieren und basierend auf den Werten der 
latitude und 
latitude zu 
MKMapSnapshotter und den gewünschten Kartenschnappschuss laden.
 override func startLoading() { guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else { fail(with: .badURL) return } load(with: queryItems) } func load(with queryItems: [URLQueryItem]) { let snapshotter = MKMapSnapshotter(queryItems: queryItems) snapshotter.start( with: DispatchQueue.global(qos: .background), completionHandler: handle ) } func handle(snapshot: MKMapSnapshotter.Snapshot?, error: Error?) { if let snapshot = snapshot, let data = snapshot.image.jpegData(compressionQuality: 1) { complete(with: data) } else if let error = error { fail(with: error) } } 
Nach dem Empfang der Daten muss das Protokoll korrekt heruntergefahren werden:
 func complete(with data: Data) { guard let url = request.url, let client = client else { return } let response = URLResponse( url: url, mimeType: "image/jpeg", expectedContentLength: data.count, textEncodingName: nil ) client.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed) client.urlProtocol(self, didLoad: data) client.urlProtocolDidFinishLoading(self) } 
Erstellen Sie zunächst ein Objekt vom Typ 
URLResponse . Dieses Objekt enthält wichtige Metadaten für die Beantwortung einer Anfrage. Dann führen wir drei wichtige Methoden für ein Objekt vom Typ 
URLProtocolClient . Die 
client Eigenschaft dieses Typs enthält jede Entität des 
URL Protokolls. Es fungiert als Proxy zwischen dem 
URL Protokoll und der gesamten 
URL Loading System , das beim 
URL Loading System dieser Methoden Rückschlüsse darauf zieht, was mit den Daten zu tun ist: Cache, Anforderungen an 
completionHandler , Protokollabschaltung irgendwie verarbeiten usw. und die Anzahl der Aufrufe dieser Methoden kann abhängig von der Protokollimplementierung variieren. Beispielsweise können wir Daten 
URLProtocolClient aus dem Netzwerk herunterladen und 
URLProtocolClient regelmäßig darüber informieren, um den Fortschritt des 
URLProtocolClient in der Schnittstelle 
URLProtocolClient .
Wenn beim Betrieb des Protokolls ein Fehler auftritt, muss 
URLProtocolClient auch korrekt verarbeitet und benachrichtigt werden:
 func fail(with error: Error) { client?.urlProtocol(self, didFailWithError: error) } 
Dieser Fehler wird dann an den 
completionHandler der Anfrage gesendet, wo er verarbeitet und dem Benutzer eine schöne Nachricht angezeigt werden kann.
4. stopLoading()
Die 
stopLoading() -Methode wird aufgerufen, wenn die Protokolloperation aus irgendeinem Grund abgeschlossen wurde. Dies kann entweder ein erfolgreicher Abschluss oder ein Fehlerabschluss oder eine Stornierung der Anforderung sein. Dies ist ein guter Ort, um belegte Ressourcen freizugeben oder temporäre Daten zu löschen.
 override func stopLoading() { } 
Damit ist die Implementierung des 
URL Protokolls abgeschlossen, das überall in der Anwendung verwendet werden kann. Fügen Sie noch ein paar Dinge hinzu, um unser Protokoll anzuwenden.
URLImageView
 class URLImageView: UIImageView { var task: URLSessionDataTask? var taskId: Int? func render(url: URL) { assert(task == nil || task?.taskIdentifier != taskId) let request = URLRequest(url: url) task = session.dataTask(with: request, completionHandler: complete) taskId = task?.taskIdentifier task?.resume() } private func complete(data: Data?, response: URLResponse?, error: Error?) { if self.taskId == task?.taskIdentifier, let data = data, let image = UIImage(data: data) { didLoadRemote(image: image) } } func didLoadRemote(image: UIImage) { DispatchQueue.main.async { self.image = image } } func prepareForReuse() { task?.cancel() taskId = nil image = nil } } 
Dies ist eine einfache Klasse, der Nachkomme von 
UIImageView , eine ähnliche Implementierung, die Sie wahrscheinlich in jeder Anwendung haben. Hier laden wir das Bild einfach über die 
URL in die Methode 
render(url:) und schreiben es in die 
image . Der Vorteil ist, dass Sie absolut jedes Bild hochladen können, entweder über die 
http / 
https URL oder über unsere benutzerdefinierte 
URL .
Um Anforderungen zum Laden von Bildern auszuführen, benötigen Sie außerdem ein Objekt vom Typ 
URLSession :
 let config: URLSessionConfiguration = { let c = URLSessionConfiguration.ephemeral c.protocolClasses = [ MapURLProtocol.self ] return c }() let session = URLSession( configuration: config, delegate: nil, delegateQueue: nil ) 
Die Sitzungskonfiguration ist hier besonders wichtig. In 
URLSessionConfiguration gibt es eine wichtige Eigenschaft für uns - 
protocolClasses . Dies ist eine Liste der Arten von 
URL Protokollen, die eine Sitzung mit dieser Konfiguration verarbeiten kann. Standardmäßig unterstützt die Sitzung die Verarbeitung von 
http / 
https Protokollen. Wenn benutzerdefinierte Unterstützung erforderlich ist, müssen diese angegeben werden. 
MapURLProtocol Sie in unserem Beispiel 
MapURLProtocol .
Sie müssen lediglich den View Controller implementieren, der Kartenschnappschüsse anzeigt. Den Quellcode finden Sie 
hier .
Hier ist das Ergebnis:
Was ist mit Caching?
Alles scheint gut zu funktionieren - bis auf einen wichtigen Punkt: Wenn wir die Liste hin und her scrollen, erscheinen weiße Flecken auf dem Bildschirm. Es scheint, dass Snapshots in keiner Weise zwischengespeichert werden und für jeden Aufruf der 
render(url:) -Methode 
MKMapSnapshotter wir 
MKMapSnapshotter Daten über 
MKMapSnapshotter . Dies braucht Zeit und daher solche Ladelücken. Es lohnt sich, einen Daten-Caching-Mechanismus zu implementieren, damit bereits erstellte Snapshots nicht erneut heruntergeladen werden. Hier nutzen wir die Leistung des 
URL Loading System URLCache , für das bereits ein Caching-Mechanismus für 
URLCache vorgesehen ist.
Betrachten Sie diesen Prozess genauer und teilen Sie die Arbeit mit dem Cache in zwei wichtige Phasen ein: Lesen und Schreiben.
Lesen
Um zwischengespeicherte Daten korrekt zu lesen, benötigt die 
URL Loading System Hilfe, um Antworten auf mehrere wichtige Fragen zu erhalten:
1. Welcher URLCache soll verwendet werden?Natürlich ist 
URLCache.shared bereits fertig, aber die 
URL Loading System URLCache.shared kann es nicht immer verwenden. Schließlich möchte der Entwickler möglicherweise seine eigene 
URLCache Entität erstellen und verwenden. Um diese Frage zu beantworten, verfügt die 
URLSessionConfiguration Sitzungskonfiguration über eine 
urlCache Eigenschaft. Es wird sowohl zum Lesen als auch zum Aufzeichnen von Antworten auf Anforderungen verwendet. Wir werden einige 
URLCache für diese Zwecke in unserer vorhandenen Konfiguration 
URLCache .
 let config: URLSessionConfiguration = { let c = URLSessionConfiguration.ephemeral c.urlCache = ImageURLCache.current c.protocolClasses = [ MapURLProtocol.self ] return c }() 
2. Muss ich zwischengespeicherte Daten verwenden oder erneut herunterladen?Die Antwort auf diese Frage hängt von der 
URLRequest Anforderung ab, die wir ausführen werden. Beim Erstellen einer Anforderung haben wir die Möglichkeit, zusätzlich zur 
URL eine Cache-Richtlinie im Argument 
cachePolicy anzugeben.
 let request = URLRequest( url: url, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 30 ) 
Der Standardwert ist 
.useProtocolCachePolicy , der ebenfalls in der Dokumentation beschrieben wird. Dies bedeutet, dass in dieser Version die Aufgabe, eine zwischengespeicherte Antwort auf eine Anforderung zu finden und ihre Relevanz zu bestimmen, vollständig in der Implementierung des 
URL Protokolls liegt. Aber es gibt einen einfacheren Weg. Wenn Sie den Wert 
.returnCacheDataElseLoad , 
.returnCacheDataElseLoad das 
URLProtocol URL Loading System beim Erstellen der nächsten Entität 
URLProtocol Teil der Arbeit: Es fragt 
urlCache zwischengespeicherten Antwort auf die aktuelle Anforderung mithilfe der Methode 
cachedResponse(for:) . Wenn zwischengespeicherte Daten vorhanden sind, wird ein Objekt vom Typ 
CachedURLResponse sofort übertragen, wenn das 
URLProtocol initialisiert und in der Eigenschaft 
cachedResponse gespeichert wird:
 override init( request: URLRequest, cachedResponse: CachedURLResponse?, client: URLProtocolClient?) { super.init( request: request, cachedResponse: cachedResponse, client: client ) } 
CachedURLResponse ist eine einfache Klasse, die Daten ( 
Data ) und Metainformationen für sie ( 
URLResponse ) enthält.
Wir können die 
startLoading Methode nur ein 
startLoading ändern und den Wert dieser Eigenschaft darin überprüfen - und das Protokoll sofort mit diesen Daten beenden:
 override func startLoading() { if let cachedResponse = cachedResponse { complete(with: cachedResponse.data) } else { guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else { fail(with: .badURL) return } load(with: queryItems) } } 
Aufzeichnen
Um Daten im Cache zu finden, müssen Sie sie dort ablegen. Das 
URL Loading System kümmert sich ebenfalls um diese Arbeit. Alles, was von uns verlangt wird, ist ihr mitzuteilen, dass wir die Daten zwischenspeichern möchten, wenn das Protokoll mithilfe der 
cacheStoragePolicy Cache-Richtlinieneinstellung 
cacheStoragePolicy . Dies ist eine einfache Aufzählung mit den folgenden Werten:
 enum StoragePolicy { case allowed case allowedInMemoryOnly case notAllowed } 
Sie bedeuten, dass das Zwischenspeichern im Speicher und auf der Festplatte nur im Speicher zulässig oder verboten ist. In unserem Beispiel geben wir an, dass das Zwischenspeichern im Speicher und auf der Festplatte zulässig ist. Warum nicht?
 client.urlProtocol(self, didReceive: response, cacheStoragePolicy: .allowed) 
Mit ein paar einfachen Schritten haben wir die Möglichkeit unterstützt, Karten-Snapshots zwischenzuspeichern. Und jetzt funktioniert die Anwendung so:
Wie Sie sehen, gibt es keine weißen Flecken mehr - die Karten werden einmal geladen und dann einfach aus dem Cache wiederverwendet.
Nicht immer einfach
Bei der Implementierung des 
URL Protokolls sind eine Reihe von Abstürzen aufgetreten.
Die erste 
URLCache die interne Implementierung der Interaktion des 
URL Loading System URLCache mit 
URLCache beim Zwischenspeichern von Antworten auf Anforderungen. In der Dokumentation 
heißt es : Trotz der 
URLCache von 
URLCache kann der Betrieb der 
cachedResponse(for:) und 
storeCachedResponse(_:for:) zum Lesen / Schreiben von Antworten auf Anforderungen zu einer 
URLCache von Zuständen führen. Daher sollte dieser Punkt in Unterklassen von 
URLCache berücksichtigt werden. Wir haben erwartet, dass mit 
URLCache.shared dieses Problem gelöst wird, aber es hat sich als falsch herausgestellt. Um dies zu beheben, verwenden wir einen separaten 
ImageURLCache Cache, einen Nachkommen von 
URLCache , in dem wir die angegebenen Methoden synchron in einer separaten Warteschlange ausführen. Als angenehmen Bonus können wir die Cache-Kapazität im Speicher und auf der Festplatte separat von anderen 
URLCache Entitäten 
URLCache .
 private static let accessQueue = DispatchQueue( label: "image-urlcache-access" ) override func cachedResponse(for request: URLRequest) -> CachedURLResponse? { return ImageURLCache.accessQueue.sync { return super.cachedResponse(for: request) } } override func storeCachedResponse(_ response: CachedURLResponse, for request: URLRequest) { ImageURLCache.accessQueue.sync { super.storeCachedResponse(response, for: request) } } 
Ein weiteres Problem wurde nur auf Geräten mit iOS 9 reproduziert. Die Methoden zum Starten und Beenden des Ladens des 
URL Protokolls können für verschiedene Threads ausgeführt werden, was zu seltenen, aber unangenehmen Abstürzen führen kann. Um das Problem zu lösen, speichern wir den aktuellen Thread in der 
startLoading Methode und führen dann den Download-Abschlusscode direkt in diesem Thread aus.
 var thread: Thread! override func startLoading() { guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = components.queryItems else { fail(with: .badURL) return } thread = Thread.current if let cachedResponse = cachedResponse { complete(with: cachedResponse) } else { load(request: request, url: url, queryItems: queryItems) } } 
 func handle(snapshot: MKMapSnapshotter.Snapshot?, error: Error?) { thread.execute { if let snapshot = snapshot, let data = snapshot.image.jpegData(compressionQuality: 0.7) { self.complete(with: data) } else if let error = error { self.fail(with: error) } } } 
Wann kann ein URL-Protokoll nützlich sein?
Infolgedessen stößt fast jeder Benutzer unserer iOS-Anwendung auf die eine oder andere Weise auf Elemente, die über das 
URL Protokoll funktionieren. Neben dem Herunterladen von Medien aus der Galerie helfen uns verschiedene Implementierungen von 
URL Protokollen beim Anzeigen von Karten und Umfragen sowie beim Anzeigen von Chat-Avataren, die aus Fotos ihrer Teilnehmer bestehen.
Wie jede Lösung hat 
URLProtocol seine Vor- und Nachteile.
Nachteile von URLProtocol
- Fehlende strikte Eingabe - Beim Erstellen einer URLSchema- und Linkparameter manuell über Zeichenfolgen angegeben. Wenn Sie einen Tippfehler machen, wird der gewünschte Parameter nicht verarbeitet. Dies kann das Debuggen der Anwendung und die Suche nach Fehlern in ihrem Betrieb erschweren. In der VKontakte-Anwendung verwenden wir spezielleURLBuilder, die die endgültigeURLbasierend auf den übergebenen Parametern bilden. Diese Entscheidung ist nicht sehr schön und widerspricht etwas dem Ziel, keine zusätzlichen Einheiten zu produzieren, aber es gibt noch keine bessere Idee. Wir wissen jedoch, dass es einen speziellenURLBuilder, der Ihnen hilft, keinen Fehler zu machen, wenn Sie eine benutzerdefinierteURLerstellen müssen.
- Nicht offensichtliche Abstürze - Ich habe bereits einige Szenarien beschrieben, die dazu führen können, dass eine Anwendung mit URLProtocolabstürzt. Vielleicht gibt es noch andere. Solche Probleme werden jedoch wie üblich entweder durch sorgfältiges Lesen der Dokumentation oder durch eingehende Untersuchung der Stapelverfolgung und Auffinden der Wurzel des Problems gelöst.
Vorteile von URLProtocol
- Schwache Komponentenkonnektivität - Der Teil der Anwendung, der das Laden der benötigten Daten initiiert, weiß möglicherweise überhaupt nicht, wie sie organisiert ist: Welche Komponenten werden dafür verwendet, wie ist das Caching angeordnet? Wir kennen nur ein bestimmtes Format URL- und interagieren nur damit.
- URLEinfache Implementierung - Für den korrekten Betrieb des Protokolls reicht es aus, mehrere einfache Methoden zu implementieren und das Protokoll zu registrieren. Danach kann es überall in der Anwendung verwendet werden.
- — , , URL-.URL,URLSession,URLSessionDataTask.
- — URL-URL-,URL Loading System.
- * API — . , API, - , URL-. , API , .URL-http/https.
URL - — . . - , - , , , — , . , , — 
URL .
GitHub