Zum Inhalt springen
Entwicklung10 min13.05.2026

Eine App gegen Doom-Scrolling bauen – während man dabei Doom-Scrollt

Wie ich mit reinem Vibe-Coding eine iOS-App gegen Doom-Scrolling entwickelt habe – und dabei fast meinen Verstand verloren hätte. Inklusive KI-Schlafberatung.

Christopher Groß
Christopher GroßFreelance Developer

Meine Frau hatte die Idee. "Eine App, die einen kurz stoppt, bevor man wieder 45 Minuten durch Instagram scrollt." Simpel. Elegant. Offensichtlich machbar.

Das war vor ein paar Monaten. Was folgte, war eines der frustrierendsten Nebenprojekte meiner Karriere – und gleichzeitig eines der lehrreichsten.

DoomStop war aber auch ein bewusster Selbstversuch: Ich wollte wissen, wie weit man mit reinem Vibe-Coding kommt. Also dem Ansatz, fast ausschließlich per KI-Prompts zu entwickeln – man beschreibt, die KI baut, man prüft die Logik. "Mit einem einzigen Prompt eine komplette App" – diesen Satz liest man überall. Ich wollte ihn testen. Auf einer echten App. Mit echten Problemen.

Spoiler: Der Satz stimmt ungefähr so sehr wie "mit einem Hammer lässt sich alles reparieren."

Was DoomStop macht

Wenn du eine konfigurierte App öffnest, läuft ein Timer. Wenn der abläuft, erscheint ein Shield – eine Sperrmaske, die dir zeigt, wie oft du die App heute schon geöffnet hast und wie viel Zeit du bereits versenkt hast. Dann: zwei Buttons. "Weitermachen" startet einen kürzeren Follow-Up-Timer. "Ich bin fertig" schickt dich raus.

Keine Bestrafung. Kein Sperren. Nur ein stiller Spiegel: Du weißt, was du tust.

Die Flutter-Illusion

Mein erster Plan war Flutter. Plattformübergreifend, eine Codebase – klug, oder?

Das Problem: Apples FamilyControls-API funktioniert ausschließlich in nativen Swift-Extensions. Kein Flutter, kein React Native, gar nichts. Also baute ich eine Flutter-App, die eine Swift-Extension aufrief, die eine andere Swift-Extension aufrief, die mit einer weiteren Swift-Extension kommunizierte. Flutter übersetzte dabei im Wesentlichen Knopfdrücke in Datenbankeinträge. Die eigentliche App? Komplett in Swift.

Nach einigen Wochen habe ich Flutter rausgeworfen. Das war die beste Entscheidung des Projekts – und gleichzeitig die ärgerlichste, weil sie so offensichtlich war.

Das Entitlement-Labyrinth

Wer Apples FamilyControls nutzen will, braucht ein Distribution Entitlement. Kein Entitlement, kein Shield, kein Monitoring – also keine App. Man beantragt es, wartet, und bekommt es irgendwann.

Irgendwann dauerte in meinem Fall knapp einen Monat.

Dann stellte sich raus: Jede der vier Extensions braucht ein eigenes Entitlement. Vier weitere Anträge. Diesmal "nur" ein paar Tage.

Was mir niemand vorher gesagt hatte – auch Claude nicht, obwohl ich explizit gefragt hatte: Das Development Entitlement und das Distribution Entitlement sind zwei komplett verschiedene Dinge. Mit dem Development Entitlement kann man Apps auswählen. Mehr nicht. Shield setzen, Monitoring starten – das geht nur mit Distribution. Das bedeutete: Jeder Test lief über TestFlight. Archivieren, hochladen, warten, installieren, testen, Fehler finden, von vorne.

Ich habe diesen Loop vermutlich 40 Mal durchlaufen.

Die API hat Meinungen. Starke Meinungen.

Hier wird es technisch – aber ich verspreche, es lohnt sich.

Eine Sache vorab: Claude recherchiert keine Dokumentation eigenständig. Ich konnte nicht einfach fragen "Was kann ich mit dieser API?" und eine verlässliche Antwort erwarten. Ich musste selbst in den Apple-Docs graben, die relevante Stelle finden und sie Claude vorlegen – erst dann entstand echtes Verständnis für das Problem. Bei einer API, die so wenig öffentlich dokumentiert ist wie FamilyControls, hat das den Debugging-Prozess massiv verlangsamt.

Erste Erkenntnis: token.localizedDisplayName ist in der Main App immer nil. Apple lässt den Entwickler nicht wissen, welchen App-Namen das überwachte Token hat. Im Shield rendert Apple den Namen selbst über ein opaque label token – der Nutzer sieht "Instagram", ich sehe davon nichts. Im Dashboard: nil. Das ist kein Bug, das ist Datenschutz. Trotzdem habe ich zu lange dagegen angekämpft.

Zweite Erkenntnis: DeviceActivityCenter.shared ist aus der ShieldActionExtension nicht erreichbar. Die Extension, die auf Nutzereingaben im Shield reagiert, kann nicht direkt mit dem Monitoring kommunizieren. Stattdessen: Darwin Notifications, UserDefaults-Signale, ein Monitor als Mittelmann. Drei Komponenten für "Wenn Nutzer auf OK tippt, Timer weiterlaufen lassen."

Dritte Erkenntnis: .defer öffnet die App – zeigt davor aber immer, unabschaltbar, einen nativen weißen iOS-Screen. Mitten in meiner dunklen Sperrmaske: weißer Blitz. Eine Weile war die Lösung .close: Shield schließen, Nutzer muss nochmal tippen, der extra Tipp als bewusste Friction. Klang sogar nach einem Feature.

Die eigentliche Lösung kam später: .defer behalten, aber die Monitor-Extension vorwarnen, den nächsten Shield zu setzen bevor .defer resolvet. Keine Lücke, kein natives Overlay. Drei Komponenten, eine Darwin Notification, ein UserDefaults-Signal – für das, was sich anhört wie: "Öffne die App."

Die App, die ihren Nutzer von Anfang an beschuldigt

Irgendwann war die Kernfunktion stabil. Dann kamen die Bugs. Und die hatten Charakter.

DoomStop zählt, wie oft du eine App geöffnet hast. Nur hat der Counter eine Weile lang so ziemlich alles gezählt – außer dem tatsächlichen Öffnen der App.

Neue App einrichten: Counter springt auf 1, bevor man die App je angefasst hat. Timer anpassen und auf Speichern drücken: Counter +1. Je fleißiger man DoomStop konfigurierte, desto schuldiger sah man auf dem eigenen Dashboard aus. Und jeden Morgen um 0:00 Uhr legte ein Logikfehler beim Tageswechsel automatisch eine UsageSession an – sodass man morgens als erstes sah: Instagram, 1x entsperrt. Im Schlaf.

Mein persönlicher Favorit war ein anderer Bug: Shield erscheint. Man klickt "Ich bin fertig", landet auf dem Homescreen. App nochmal öffnen. Shield erscheint wieder. Nochmal klicken. Diesmal: App öffnet sich ohne Shield. Und danach: kein Shield mehr für diese App. Permanent.

Man konnte seinen eigenen Schutzschild durch zweimaliges Antippen wegklicken. Für eine Selbstkontroll-App ist das besonders komisch – der Nutzer, der am schwersten kämpft, wurde am wenigsten geschützt.

Tests: die Lektion, die ich zu spät gelernt habe

Irgendwann war das Muster unübersehbar: Wir beheben einen Bug, erzeugen zwei neue. Claude fixte Extension A, Extension B fing an zu spinnen. Jeder Fix war Roulette.

Der Grund war strukturell – der Code war nicht testbar. Also haben wir alles einmal sauber umgeschrieben: Dependency Injection, klare Verantwortlichkeiten, eine echte Unit-Test-Suite. Das Refactoring erzeugte keinen einzigen neuen Bug. Nicht einen. Und seitdem ist die Bug-Rate spürbar gesunken.

Die Erkenntnis ist nicht neu: Tests früh einführen, nicht wenn das System schon brennt. Ich hätte das direkt nach dem ersten stabilen Prototypen tun sollen. Habe ich nicht. Habe dafür bezahlt.

3 Uhr nachts – und der KI-Schlafberater

Der Follow-Up-Timer erschien nicht konsistent. Manchmal sofort, manchmal gar nicht, manchmal erst nach einem App-Neustart. Wir kreisten seit Stunden darum herum. Und dann – ich konnte es spüren – waren wir nah dran.

Und dann erschien das auf meinem Screen:

"Ich würde empfehlen, für heute Pause zu machen. Manchmal hilft frische Augen. Schlaf hilft beim Problemlösen."

Es war 3 Uhr nachts. Ich war kurz vor dem Durchbruch. Mein KI-Pair-Programmer hat mir empfohlen, ins Bett zu gehen.

Claude hat sogar nachgehakt: "Ich verstehe, aber nach intensiver Debugging-Session kann Schlaf Wunder wirken..."

Ich habe weitergemacht.

Was wir gefunden haben: Drei gleichzeitige Root Causes. intervalDidStart löschte aktiv den Shield, obwohl es das nicht sollte. Falscher completionHandler in der falschen Extension. Und das Absurdeste: Die Zeit, die ich in der DoomStop-App verbracht hatte, wurde vom Follow-Up-Budget der überwachten App abgezogen. Meine eigene App hat mich für die Nutzung meiner eigenen App bestraft.

Wir haben das Problem noch in dieser Nacht gelöst.

Was bleibt

DoomStop ist noch nicht veröffentlicht. Es läuft als interne Beta, und es funktioniert – der Shield erscheint, die Timer laufen, die Daten stimmen.

Was ich mitgenommen habe: Vibe-Coding funktioniert gut für vieles. Für Boilerplate, für UI-Logik, für schnelle Iteration. Für eine iOS-App, die tief in Apples Screen-Time-API eingreift? Da muss man selbst verstehen, was man baut. Die KI schreibt schneller als ich – aber sie kennt nur, was ich ihr zeige. Bei undokumentierten API-Eigenheiten, bei Caching-Verhalten, bei dem Zusammenspiel von fünf Extensions: Recherche muss man selbst machen.

"Mit einem Prompt eine komplette App bauen" – stimmt nicht. Was stimmt: Mit einem guten Workflow und viel Kontext-Engineering kann man eine App bauen, die ohne KI-Assistenz deutlich länger gedauert hätte. Das ist immer noch beeindruckend genug.

Und der KI-Schlafberater hat übrigens nicht recht gehabt. Nur damit das klar ist.

Christopher Groß
$ whoami

Christopher Groß

Fullstack Developer & AI Orchestrator aus Hamburg

Christopher Groß baut seit über 20 Jahren Webanwendungen für Startups und Agenturen. Sein Fokus liegt auf Vue.js, Nuxt und AI-gestützter Entwicklung. Er glaubt an sauberen Code, klare Specs und Kaffee in rauen Mengen.

Lust auf mehr?

Du willst wissen wie ich arbeite und was mich antreibt? Schreib mir – 30 Minuten, ein echtes Gespräch über Technik, AI und Projekte.

Termin buchen

oder schreib mir direkt: moin@grossbyte.io

Antwort in 24h
Entspanntes Gespräch
Keine Verpflichtung