Auf einem entfernten Entwicklungs-Mac liegt ein Fehlschluss nahe: Wenn der Datei-Upload im Vordergrund des iOS-Simulators gelingt, werde der Auftrag auch beim Wechsel in den Hintergrund nicht verloren gehen. Bei Hintergrunduploads spielen jedoch die Datei auf dem Datenträger, die vom System übernommene Übertragungsaufgabe und die Callbacks beim Wiederaufnehmen der App zusammen. Ob die Anfrage abgeschickt wurde, reicht als Prüfung nicht aus. Für die erste Abnahme dient ein lokaler Empfänger auf dem Cloud-Mac; die Prüfungen am echten Gerät bleiben eine eigene Liste.
Umfang der Prüfung festlegen
Geprüft werden Datei-Upload-Aufgaben, die mit URLSessionConfiguration.background erstellt wurden – nicht Uploads, bei denen Data im Speicher bleibt, bis das Netzwerk wieder verfügbar ist. Bereiten Sie eine Beispieldatei mit gut unterscheidbarer Größe und ohne echte Zugangsdaten vor. Notieren Sie Bytezahl und SHA-256. Der Server muss mindestens Anfragemethode, empfangene Bytezahl und Antwortstatus protokollieren. Auf Clientseite werden Aufgabenkennung, Übertragungsfehler und HTTP-Statuscode getrennt erfasst.
Ein Fehlerwert von
nilindidCompleteWithErrorbedeutet nur, dass die Transportschicht keinen Fehler gemeldet hat. Der Server kann trotzdem mit 4xx oder 5xx antworten. Zur Abnahme gehört deshalb auch die Prüfung der HTTP-Antwort.
Der Simulator eignet sich, um diesen grundlegenden Ablauf zu prüfen. Er belegt weder, dass das System die App unter bestimmten Bedingungen erneut startet, noch ersetzt er Tests mit tatsächlichen Wechseln im Mobilfunknetz.
Minimalen Empfänger auf dem Mac starten
Speichern Sie das folgende Skript als receiver.py und führen Sie es auf demselben Mac aus, auf dem der Simulator läuft. Es lauscht nur auf der Loopback-Adresse, begrenzt die Größe einzelner Anfragen und schreibt empfangene Daten an einen festen Testpfad. Es ist kein Upload-Dienst für das öffentliche Internet.
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
LIMIT = 10 * 1024 * 1024
OUTPUT = Path("/tmp/ios-upload-received.bin")
class Receiver(BaseHTTPRequestHandler):
def do_POST(self):
length = int(self.headers.get("Content-Length", "0"))
if self.path != "/upload" or not 0 < length <= LIMIT:
self.send_error(400)
return
body = self.rfile.read(length)
if len(body) != length:
self.send_error(400)
return
OUTPUT.write_bytes(body)
self.send_response(201)
self.end_headers()
HTTPServer(("127.0.0.1", 8765), Receiver).serve_forever()
Starten Sie es mit python3 receiver.py. Wenn die Test-App http://127.0.0.1:8765/upload aufruft, konfigurieren Sie nur im Test-Build eine eng begrenzte App-Transport-Security-Ausnahme für localhost oder den verwendeten Loopback-Host. Release-Builds sollten weiterhin HTTPS verwenden. Falls der Simulator keine Verbindung herstellen kann, prüfen Sie zuerst, ob der Empfänger noch läuft und der Port übereinstimmt. Kontrollieren Sie danach die Ausnahmekonfiguration, statt sofort die Upload-Logik zu ändern.
Hintergrundaufgabe aus einer Datei auf dem Datenträger erstellen
Das folgende Beispiel zeigt die wesentlichen Schritte zum Erstellen der Aufgabe. Der aufrufende Code muss eine Datei-URL übergeben, deren Datei vollständig geschrieben wurde und für den aktuellen Prozess lesbar ist. endpoint kommt aus der Testkonfiguration; die lokale Testadresse gehört nicht fest in den Release-Code. Die Kennung der Hintergrundsitzung muss für diesen Upload-Ablauf stabil bleiben. Erzeugen Sie nicht bei jedem Start eine neue Kennung.
import Foundation
final class UploadCoordinator: NSObject, URLSessionDelegate, URLSessionTaskDelegate {
private lazy var session: URLSession = {
let config = URLSessionConfiguration.background(
withIdentifier: "com.example.upload-check"
)
config.isDiscretionary = false
config.sessionSendsLaunchEvents = true
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
func upload(file: URL, endpoint: URL) {
var request = URLRequest(url: endpoint)
request.httpMethod = "POST"
session.uploadTask(with: request, fromFile: file).resume()
}
func urlSession(
_ session: URLSession,
task: URLSessionTask,
didCompleteWithError error: Error?
) {
let status = (task.response as? HTTPURLResponse)?.statusCode
let accepted = error == nil && status == 201
recordResult(taskID: task.taskIdentifier, accepted: accepted)
}
func urlSessionDidFinishEvents(
forBackgroundURLSession session: URLSession
) {
finishPendingBackgroundEvents()
}
private func recordResult(taskID: Int, accepted: Bool) {
// Ergebnis an die persistente Statusverwaltung der App übergeben
}
private func finishPendingBackgroundEvents() {
// Gespeicherten System-Completion-Handler nach der Statusaktualisierung aufrufen
}
}
Die beiden privaten Methoden sind Integrationspunkte der App, keine vollständige Implementierung des Uploads. Im produktiven Code muss das persistierte Ergebnis die Zuordnung zwischen Aufgabe und zugehöriger Datei bewahren. Wenn der App-Delegate Ereignisse der Hintergrundsitzung erhält, muss er die Sitzung mit derselben Kennung neu erstellen, den Delegate wieder anbinden und den vom System übergebenen Completion-Handler unter der Kennung speichern. Rufen Sie diesen Handler erst auf, nachdem die Ereignisse verarbeitet und der UI-Status aktualisiert wurden. Löschen Sie eine noch nicht überprüfte Datei nicht allein aufgrund eines einzelnen Erfolgs-Callbacks.
In Etappen prüfen statt erzwungenes Beenden mit Hintergrundbetrieb zu verwechseln
Lassen Sie die App zunächst im Vordergrund und senden Sie eine Beispieldatei. Nachdem der Empfänger 201 zurückgegeben hat, vergleichen Sie den SHA-256-Wert von /tmp/ios-upload-received.bin mit dem der Originaldatei. Nur wenn beide übereinstimmen, ist für diesen Durchlauf der Empfang des vollständigen erwarteten Inhalts belegt. Senden Sie anschließend eine andere Beispieldatei und wechseln Sie über die Simulator-Oberfläche zum Home-Bildschirm. Warten Sie auf den Callback und prüfen Sie die Datei erneut. Beenden Sie den Prozess nicht per Befehl, um einen gewöhnlichen Wechsel in den Hintergrund nachzustellen: Ein vom Nutzer erzwungenes Beenden ist ein anderer Fall als die Wiederaufnahme, nachdem das System eine Aufgabe übernommen hat.
| Prüfpunkt | Zu dokumentierende Nachweise |
|---|---|
| Aufgabe erstellen | Sitzungskennung, Aufgabenkennung, Pfad und Bytezahl der Quelldatei |
| Empfang auf dem Server | HTTP-Status, tatsächlich empfangene Bytezahl und SHA-256 der Datei |
| Abschluss auf dem Client | Übertragungsfehler und HTTP-Status getrennt erfasst |
| Wiederaufnahme der App | Neuerstellung der Sitzung, Eingang des Callbacks und Zeitpunkt des Aufrufs des Completion-Handlers |
Die Abnahme auf einem echten Gerät muss zusätzlich einen gesperrten Bildschirm, die Wiederherstellung nach einer Netzwerkunterbrechung, den Wechsel des Netzwerktyps und einen erneuten App-Start nach einer Beendigung durch das System abdecken. Ein vom Nutzer erzwungenes Beenden gehört in einen separaten Test; sein Ergebnis ist kein Beleg für gewöhnliches Hintergrundverhalten. Wenn vom entfernten Mac aus kein Gerät vor Ort angeschlossen werden kann, geben Sie die Simulator-Nachweise und die Liste offener Tests an eine Kollegin oder einen Kollegen mit echtem Gerät weiter. Melden Sie nicht, die Gerätetests seien bestanden.
Häufige Fehler und Aufräumen nach dem Test
Wenn auf dem Server keine Anfrage ankommt, prüfen Sie zuerst, ob die Datei-URL auf eine tatsächlich vorhandene reguläre Datei verweist. Kontrollieren Sie danach Testadresse, Port und Transportsicherheitskonfiguration. Wenn die Datei ankommt, der Client die Aufgabe aber als fehlgeschlagen markiert, prüfen Sie den Antwortstatus: Das Beispiel akzeptiert nur 201. „Kein Übertragungsfehler“ bedeutet nicht „vom Server angenommen“. Bleibt nach der Wiederaufnahme der App ein Callback aus, prüfen Sie, ob die Hintergrundsitzung mit ihrer ursprünglichen Kennung neu erstellt wurde, das Delegate-Objekt weiterhin gehalten wird und der Completion-Handler nicht zu früh aufgerufen wurde.
Beenden Sie zum Schluss den Testempfänger, löschen Sie /tmp/ios-upload-received.bin und entfernen Sie Quelldateien sowie Netzwerkausnahmen, die nur für den Test benötigt wurden. Für den produktiven Ablauf müssen außerdem eine Obergrenze für Wiederholungsversuche und der Umgang des Servers mit doppelten Einsendungen festgelegt werden. Dateien sollten erst gelöscht werden, nachdem das Ergebnis persistent gespeichert wurde. So erhält die nächste Fachkraft nicht bloß die Aussage „Hintergrunduploads funktionieren“, sondern wiederholbare Schritte, überprüfbare Nachweise und eine Liste der noch ausstehenden Gerätetests.
Häufig gestellte Fragen
Kann eine Background-URLSession direkt Daten aus dem Arbeitsspeicher hochladen?
Für diesen Ablauf verwenden Sie eine Datei als Quelle. Schreiben Sie die Nutzdaten zuerst auf den Datenträger und erstellen Sie die Aufgabe mit uploadTask(with:fromFile:).
Beweist ein erfolgreicher Simulatorlauf die Wiederaufnahme auf einem Gerät?
Nein. Der Simulator prüft Anfrage, Antwort und grundlegende Callbacks. Systemstart der App, Netzwechsel und das Verhalten nach erzwungenem Beenden müssen separat auf einem Gerät geprüft werden.
Ein Mac für Ihre nächste Remote-Entwicklung
Wählen Sie Modell und Standort passend zu Ihrem Vorhaben und verbinden Sie sich per Bildschirmfreigabe, VNC oder SSH. Preise und Verfügbarkeit sehen Sie aktuell in der Konsole.