PixzlSwiftLens – unser Open-Source-Debug-HUD für SwiftUI
Mit einem Modifier bekommt jede SwiftUI-App FPS, RAM, CPU, Netzwerkaufrufe, OSLog-Stream und live View-Rerender-Raten ins HUD. Open Source, MIT, ab heute auf GitHub.

Was wir gebaut haben
Wir haben in den letzten Wochen ein Werkzeug gebraucht, das wir in keinem der etablierten iOS-Debug-HUDs gefunden haben: eine Live-Anzeige, welche SwiftUI-Views gerade zu oft rerendern. Genau das ist jetzt ein eigenes Open-Source-Paket geworden – PixzlSwiftLens, MIT-lizenziert, ab heute unter github.com/Pixzl/PixzlSwiftLens verfügbar. v0.3.0 ist heute Vormittag durchgelaufen und ist gleichzeitig die erste Version, die wir als „extern brauchbar" einstufen.
Das HUD lebt komplett in deiner App. Du hängst einen einzigen Modifier an deine Root-Scene, und ab da reicht ein Schütteln des Geräts, um eine voll ausgestattete Diagnose-Oberfläche aufzuklappen:
import PixzlSwiftLens
@main
struct MyApp: App {
var body: some Scene {
WindowGroup { ContentView() }
.pixzlSwiftLens()
}
}
Eine Zeile. Im Release-Build wird sie zu self, der Rest der Bibliothek ist dort gar nicht erst eingelinkt – dazu unten mehr.
Das eigentliche Killer-Feature: Views, die zu oft rerendern
Die größte Performance-Falle in SwiftUI ist nicht langsamer Code, sondern Bodies, die hundertmal pro Sekunde neu evaluiert werden, weil irgendein @Observable weiter oben im Baum zu breit zugeschnitten ist. Self._printChanges() in der Console hilft punktuell, aber ist nicht aggregierbar. Xcode Instruments hat den Daten-Tiefenblick, aber der Aufwand für „nur kurz schauen, was gerade heiß läuft" ist enorm. Was uns gefehlt hat, war eine permanente, in der laufenden App eingebettete Tabelle.
PixzlSwiftLens legt genau dafür einen winzigen Modifier nach:
CartView()
.lensTrack("Cart")
Im laufenden Betrieb schüttelst du das Gerät, tippst auf Views, und siehst eine Tabelle, die sich alle 500 ms aktualisiert:
| View | total | rate |
|---|---|---|
| Cart | 842 | 38.0/s – hot |
| Header | 128 | 1.0/s |
| ProductRow | 64 | 0.2/s |
Sortierung läuft automatisch nach Live-Rate. Alles über fünf Invalidations pro Sekunde wird orange markiert, alles über fünfzehn rot mit „hot"-Badge. In der Praxis findet man so innerhalb weniger Minuten die Stelle, an der ein zu breit gefasster @Observable einen ganzen Subtree mitreißt – und kann gezielt zerschneiden.
Was sonst drin ist
Neben dem Views-Panel sind drei weitere Module aktiv, sobald das HUD geöffnet ist.
Das Performance-Panel zeigt FPS, RAM und CPU mit rollierenden 60-Sekunden-Charts. Für RAM messen wir phys_footprint, also genau die Metrik, die der iOS-Jetsam-Watchdog beobachtet – nicht die irreführende „resident size", die in vielen Tutorials genannt wird.
Das Netzwerk-Panel schneidet jeden URLSession-Call mit. Inline-Status, „Copy as cURL", JSON-Pretty-Print und ein hartes 256-KB-Cap pro Body, damit der Recorder nicht zur RAM-Falle wird. Für URLSession.shared und ähnliche Ambient-Traffic-Aufrufe schaltest du den Recorder einmalig im App-Init frei:
@main
struct MyApp: App {
init() { PixzlSwiftLensNetwork.install() }
var body: some Scene {
WindowGroup { ContentView().pixzlSwiftLens() }
}
}
Das Logs-Panel ist ein Live-Reader von OSLogStore für den aktuellen Prozess, mit Level-Filter und Volltextsuche. Der Cursor läuft auf einer monotonen Uptime-Uhr, nicht auf der Wall-Clock – das klingt akademisch, ist es aber nicht: bei NTP-Resync oder Zeitzonenwechsel im Simulator hatten wir vorher Logs gesehen, die scheinbar zurück in der Zeit sprangen.
Warum nicht Pulse, FLEX oder Atlantis
Die Frage haben wir uns selbst dreimal gestellt, bevor wir eine eigene Bibliothek angefangen haben. Pulse ist beim Netzwerk-Detail tiefer als wir und behauptet das auch nicht anders zu sein – wer ein vollwertiges Network-Inspection-Werkzeug sucht, ist dort gut aufgehoben. FLEX ist UIKit-zentriert und fühlt sich in einer reinen SwiftUI-App immer wie ein Fremdkörper an. Atlantis braucht eine Desktop-App parallel.
Was uns an allen dreien fehlt: die Reibungsarmut der Installation und der View-Render-Blick. Pulse braucht zwischen Setup, eigenen Logger-Bridges und Custom-URLProtocol gut zehn Code-Stellen, bis es im Build steht. PixzlSwiftLens ist eine Zeile, plus optional eine zweite für Network-Capture, plus pro beobachteter View ein Modifier. Und die Live-Tabelle, welche Views wie heiß sind – die haben wir in keinem der drei gefunden.
Wir wollen Pulse nicht ersetzen. Wir wollen die Lücke schließen, die zwischen „eingebauter Logger-Output in der Console" und „voller Pulse-Stack" klafft – und die SwiftUI-Performance-Diagnose dort hinpacken, wo sie hingehört: in die laufende App.
Im Release-Build verschwindet alles
Genauso wichtig wie die Funktion ist, was im Release-Build passiert: nichts. Der öffentliche Modifier, das Network-Protocol und .lensTrack kollabieren außerhalb von #if DEBUG zu self. Es gibt im Release-Binary kein Symbol für PixzlSwiftURLProtocol, URLSessionSwizzler oder LensTrackModifier. Verifizieren kannst du das selbst:
xcrun nm -gU YourApp.app/YourApp | grep -i "PixzlSwiftURLProtocol\|LensTrack"
# (keine Ausgabe)
Das ist nicht „leichtgewichtig". Das ist Null-Code im versendeten Build. Für App-Store-Reviewer-Paranoia und für jeden, der bei Performance-Tools zu Recht skeptisch ist, war uns das wichtig – ein Debug-HUD darf in Production nicht einmal einen Symbol-Eintrag rechtfertigen müssen.
Installation
Über den Swift Package Manager:
.package(url: "https://github.com/Pixzl/PixzlSwiftLens.git", from: "0.3.0")
Anschließend "PixzlSwiftLens" als Dependency an dein Target hängen. Die drei Quickstart-Schritte – Modifier dranhängen, Network-Recorder optional installieren, .lensTrack an die verdächtigen Views – sind auch im README durchgespielt, inklusive Konfigurations-Optionen für Aktivierung (.shake, .threeFingerTap, .floatingButton), Position des Pills und Auswahl der aktiven Panels.
iOS 26, Swift 6.2 – und warum wir nicht weiter zurückgehen
PixzlSwiftLens setzt iOS 26, Swift 6.2 und Xcode 26 voraus. Das ist die gleiche harte Linie, die wir auch bei Deploir gezogen haben, und der Grund ist hier wie dort: das, was wir intern verwenden – Strict Concurrency, das @Observable-Tracking für die View-Rate, die aktuelle Form von OSLogStore – funktioniert auf älteren Versionen entweder gar nicht oder nur mit Workarounds, die das Paket aufblähen würden. Konsistenz über alle Pixzl-Tools ist uns wichtiger als Backward-Compat um jeden Preis.
Was als nächstes kommt
Auf der Roadmap steht ein tieferes Network-Panel mit Header- und Body-Diff zwischen aufeinanderfolgenden Calls auf dieselbe Route, ein optionales SwiftData-Panel für Modell-Snapshots im Kontext, und mittelfristig ein Mac-Companion-Build für visionsuche. Issues sind offen, PRs willkommen – das Repo lebt von realen Use-Cases, und wir bauen die Diagnose-Bausteine, die wir in unseren eigenen Apps brauchen.
PixzlSwiftLens läuft heute schon in Deploir und in der Pixzl-App im Debug-Build mit. Wenn du SwiftUI-Apps baust und deinen Body-Re-Render-Counter live haben willst: das Repo liegt unter github.com/Pixzl/PixzlSwiftLens. MIT-Lizenz, ein Modifier, raus aus dem Release-Build.
Beitrag teilen
Text des Beitrags
Plain-Text zum Mitnehmen – nur zur privaten Nutzung
Bilder im Beitrag
Alle Grafiken als ZIP – nur zur privaten Nutzung
Newsletter
Mehr Artikel wie diesen per Mail
Monatliche Insights zu Webentwicklung, Apps, Shopware und KI. Kein Spam, jederzeit abmeldbar.
Du bekommst gleich eine Bestätigungsmail. Bitte klicke den Link darin, um deine Anmeldung abzuschließen. Kein Spam, Abmeldung jederzeit. Datenschutzerklärung