Warum eine angeklickte Artikelseite nur zur Haelfte lud: Die Bild-Route wird je Tabellenzeile einmal aufgerufen, und ein Browser haelt je Host nur rund sechs Verbindungen offen. Was diese Route aufhaelt, legt die ganze Oberflaeche lahm. Sie hielt gleich dreifach auf: 1. Fehlte die lokale Kopie, holte ``images.ensure`` das Bild MITTEN in der Anfrage von der fremden Adresse - bis zu 15 Sekunden, je Zeile, und bei jedem Aufruf erneut, weil ein Fehlschlag nirgends vermerkt wurde. Artikel aus einem Backup-Import haben genau diese Ausgangslage. Gemessen: 2,01 s je Aufruf, dreimal hintereinander. Jetzt: 0,01 s, das Nachholen laeuft nach der Antwort im Hintergrund und ein toter Link wird eine Stunde gesperrt. 2. Ein Vorschaubild ist 34x34 Pixel gross - ausgeliefert wurde das Original. Gemessen: 2530 KB je Briefmarke, bei 40 Zeilen 99 MB fuer eine Liste. Neu erzeugt die Bild-Route (Pillow) eine Vorschau und legt sie daneben ab; ``?thumb=1`` liefert sie aus. Gemessen: 0,6 KB. 3. If-None-Match wurde ignoriert. Nach den 5 Minuten Cache-Frist lud der Browser jedes Bild komplett neu - das erklaert, warum es "alle 20 Minuten" wieder losging. Jetzt 304 ohne Daten. Dazu im Web: laufende Bildabrufe werden beim Seitenwechsel abgebrochen (sonst steht die neue Seite hinter Bildern Schlange, die niemand mehr sieht), und wo ``image_version`` bekannt ist, entfaellt die Anfrage fuer bildlose Artikel ganz. Und der Verbindungsvorrat der Datenbank: 5 (+10) gegen 40 Arbeits-Threads von FastAPI. Ab der 16. gleichzeitigen Anfrage wartete eine Route stillschweigend 30 Sekunden - von aussen ein haengender Server. Jetzt 10 (+20) mit 10 Sekunden Frist: lieber ein Fehler als eine halbe Minute Stille. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Vorrania – iOS-App
Native SwiftUI-App für Lebensmittel und Gegenstände. Sie spricht dieselbe REST-API wie die Web-Oberfläche.
Stand: Voll nutzbar. Login; Scannen von Barcode, MHD/Datum (per Texterkennung, auch nur Tag+Monat ohne Jahr), Lagerort-QR und Name/Marke; Ein-/Auslagern mit mehreren Chargen bzw. Menge je Lagerort; Einzelstücke mit UID/QR, Kaufdatum, Garantie und Beleg; Verbrauchsgegenstände als Charge; Artikel anlegen (Open Food/Products Facts oder manuell, mit Foto); getrennte Listen für Lebensmittel/Gegenstände/Einzelstücke, Kategoriefilter, Sortierung; Einkaufsliste, Ablaufliste, Mindestbestände; Verlauf mit Sprung zum Produkt; lokale Dashboards und Push-Benachrichtigungen bei ablaufenden Produkten.
Projekt in Xcode öffnen
Variante A – mit XcodeGen (empfohlen)
brew install xcodegen
cd ios
xcodegen generate
open Vorrania.xcodeproj
project.yml beschreibt das Projekt vollständig (Bundle-ID, Info.plist,
Shortcuts, URL-Schema).
Variante B – ohne XcodeGen
- Xcode → File ▸ New ▸ Project… → App, Interface SwiftUI, Sprache Swift
- Produktname
Vorrania, Bundle-ID z. B.com.scarriffle.vorrania - Die von Xcode erzeugte
ContentView.swiftund…App.swiftlöschen - Den Ordner
Sources/per Drag & Drop ins Projekt ziehen („Copy items if needed") - In den Target-Einstellungen die mitgelieferte
Sources/Info.plistals Info.plist setzen (Build Settings ▸ Info.plist File) und Generate Info.plist File auf No stellen
Auf dem iPhone installieren
- iPhone per Kabel verbinden, in Xcode als Ziel auswählen
- Signing & Capabilities → dein Apple-Developer-Team wählen
- ▶︎ Run. Beim ersten Start auf dem iPhone unter Einstellungen ▸ Allgemein ▸ VPN & Geräteverwaltung dem Entwickler vertrauen
Erste Schritte in der App
Beim Start nach der Server-Adresse fragen lassen, z. B. http://192.168.1.50:8080
(dieselbe Adresse wie im Browser), dann mit deinem Benutzer anmelden. Adresse und
Token bleiben gespeichert – das Token liegt im Keychain.
Home-Screen-Shortcuts
Es gibt zwei Wege direkt in den Scan-Bildschirm:
Schnellaktionen – langer Druck auf das App-Symbol zeigt „Einlagern" und „Auslagern" (funktioniert ohne weitere Einrichtung).
Eigene Symbole auf dem Home-Bildschirm – über die Apple-App Kurzbefehle:
- Kurzbefehle ▸ + ▸ Aktion URL öffnen
- URL
vorrania://checkin(bzw.vorrania://checkout) eintragen - Kurzbefehl benennen, dann ▸ Zum Home-Bildschirm hinzufügen
Beide Wege öffnen die App direkt in der Kamera.
Aufbau der Quellen
| Datei | Inhalt |
|---|---|
VorraniaApp.swift |
App-Einstieg, Schnellaktionen, URL-Schema |
Session.swift |
Server-Adresse (UserDefaults) und Token (Keychain) |
APIClient.swift |
REST-Aufrufe gegen /api/... |
Models.swift |
Codable-Typen passend zu backend/app/schemas.py |
ScannerView.swift |
Kamera + Barcode-Erkennung (EAN-8/13, UPC-E, Code128, QR) |
RootView.swift |
Startbildschirm und Routing |
LoginView.swift |
Server + Anmeldung |
CheckInView.swift / CheckInFormView.swift |
Scan → Charge (MHD), Menge je Lagerort oder Einzelstück |
CheckOutView.swift |
Scan → Menge, Chargenauswahl oder FEFO |
ProductViews.swift |
Artikelsuche und Anlegen (mit OFF-Vorbefüllung, Foto, Text-Scan) |
ProductDetailView.swift |
Produkt bearbeiten, Verwaltungsart ändern, Chargen korrigieren |
ObjectStockView.swift |
Gegenstände: Menge je Lagerort (hinzufügen/umlagern/entfernen) |
ItemViews.swift / ItemListView.swift |
Einzelstücke anlegen, bearbeiten und auflisten |
AssignScanView.swift |
Einzelstück (Scan oder Liste) einem Lagerort-QR zuordnen |
ListViews.swift |
Produktlisten, Einkaufsliste, „bald ablaufend", Mindestbestände |
DashboardView.swift / DashboardCards.swift / ChartCards.swift |
Übersicht mit Karten und Diagrammen |
NotificationScheduler.swift / NotificationSettings*.swift |
Push-Benachrichtigungen bei Ablauf |
DateScanView.swift / BestBeforeText.swift |
MHD/Text per Texterkennung ablesen |
DisplaySettings.swift |
Datumsformat und Einheiten-Beschriftungen vom Server |
CategoryPicker.swift |
Kategorie-Auswahl und Gruppe anlegen |
Gruppe und Kategorie – zwei verschiedene Dinge
Gruppe dient der Bestandsrechnung: Mehl von Rewe, Aldi und Migros zählen zusammen, wichtig ist „ich habe 5 kg Mehl“. Eine Gruppe hat deshalb einen gemeinsamen Mindestbestand mit Einheit und EAN-Codes. Ordnest du einen Artikel einer Gruppe zu, erscheint sein EAN-Code automatisch dort; wechselt er die Gruppe, wandert der Code mit.
Kategorie dient allein dem Überblick („Süßwaren“, „Molkereiprodukte“) und ist verschachtelbar wie ein Lagerort (Süßwaren → Schokolade). Sie hat keinen Bestand, keinen Mindestbestand und keine EAN-Codes. Filterst du auf eine Oberkategorie, erscheinen die Artikel ihrer Unterkategorien mit. Beim Anlegen schlägt der Server eine Kategorie aus der Open-Food-Facts-Einordnung vor.
Weiteres
Push-Benachrichtigungen bei Ablauf und Lagerort je Charge beim Einlagern sind umgesetzt. Weitere Ideen stehen in ../docs/ROADMAP.md.