Case study 01 · 2026
Een native macOS-app in SwiftUI die per project je processen, quick actions en macro's beheert — zodat drie terminaltabs er één worden.
Rol
Solo developer
(eigen product)
Duur
doorlopend
juli 2026 — heden
Stack
Swift · SwiftUI · SwiftData
macOS 26 · XCTest
01 — Het probleem
Elk Laravel-project dat ik open kost drie terminaltabs: een webserver, een queue worker en Vite. Ze weten niets van elkaar, ze onthouden niets, en na een herstart begin je opnieuw met uitzoeken welke tab welke was.
De commando's ertussenin — migreren, tests draaien, een sitemap genereren — typ je elke keer opnieuw, in de juiste map, met de juiste PATH. Geen moeilijk probleem. Wel een dat elke dag twintig keer terugkomt.
02 — De aanpak
Je wijst een projectmap aan en de ProjectFolderDetector leest wat er staat: een artisan-bestand, de scripts in package.json, een docker-compose. Daaruit komt een voorstel — Laravel, Herd, Vite, Node of Docker Compose — met de commando's die daarbij horen, klaar om te bevestigen.
Wat overblijft is één venster per project: langlopende processen als kaarten met hun status, quick actions in secties die je zelf indeelt, en macro's die een reeks stappen op volgorde afwerken. SwiftData houdt het vast, zodat het er morgen nog net zo bij staat.
03 — Onder de motorkap
De eerste versie had een lijstje met paden: Homebrew, /usr/local, Herd. Dat dekt veel en mist hele categorieën — nvm, asdf, mise, pyenv, rbenv, Volta, en alles wat iemand zelf op zijn PATH heeft gezet. Als het misgaat faalt een commando dat in Terminal gewoon werkt hier met command not found, en dat is het meest verwarrende wat deze app iemand kan aandoen.
Dus vraagt Vormik het gewoon: één keer je login shell starten en teruglezen wat PATH is. Dat vangt ook de version manager die volgend jaar bedacht wordt, juist omdat er nergens een tool bij naam herkend wordt. De prijs is een timeout — een zware .zshrc kan seconden duren, en het ophalen van de omgeving mag nooit zijn wat de app traag laat voelen.
nonisolated static func resolve(timeout: TimeInterval = 5) -> ShellEnvironment? {
let shellPath = ProcessInfo.processInfo.environment["SHELL"] ?? "/bin/zsh"
guard let output = ProcessRunner.run(
executable: shellPath,
arguments: ["-l", "-c", "echo $PATH"],
timeout: timeout
) else {
return nil
}
let searchPaths = parsePath(output)
guard !searchPaths.isEmpty else { return nil }
return ShellEnvironment(shellPath: shellPath, searchPaths: searchPaths)
}
04 — De vorm van het ding
3
terminaltabs die het per project vervangt
1×
login shell starten, voor de PATH die jij echt hebt
0
sandbox — het start /bin/zsh zelf, dus geen App Store
Wat ik zou veranderen
ContentView is inmiddels opgesplitst in zes extensies en tóch is de persistentielaag nog ruim dertienhonderd regels. Daar zou ik opnieuw beginnen: dat hoort achter een service te zitten die niets van SwiftUI weet, precies zoals de rest van Services/ dat al doet.
Het tweede is geen code. Vijf euro per maand vragen voor een lokale tool zonder backend, aan ontwikkelaars die weten dat er geen backend is, is een lastig verhaal. Eenmalig kopen met twaalf maanden updates past beter bij wat dit werkelijk is — en dat wil je beslissen vóór je de licentiecode schrijft, niet erna.