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
URL
Schema- 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 spezielle URLBuilder
, die die endgültige URL
basierend 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 speziellen URLBuilder
, der Ihnen hilft, keinen Fehler zu machen, wenn Sie eine benutzerdefinierte URL
erstellen müssen. - Nicht offensichtliche Abstürze - Ich habe bereits einige Szenarien beschrieben, die dazu führen können, dass eine Anwendung mit
URLProtocol
abstü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. URL
Einfache 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