gateway/.cursor/plans/swift_ios_app_nachbau_3dc75f35.plan.md
Stephan Schellworth ea566c270f docs: add gateway docs and Cursor plan artifacts
Made-with: Cursor
2026-04-22 07:21:43 +02:00

38 KiB

name overview todos isProject
Swift iOS App Nachbau Vollstaendiger Implementierungsplan fuer den Nachbau des React-Web-Frontends (frontend_nyla) als native Swift/SwiftUI iOS 18+ App fuer iPhone und iPad. Baut auf dem bestehenden Plan auf, korrigiert Fehler und integriert alle geklaerten Entscheidungen (kein Backend-Aenderungen, kein Voice, kein Push, ASWebAuthenticationSession fuer OAuth, read-only Billing, nativer Code-Editor).
id content status
phase-0 Phase 0: Xcode-Projekt erstellen, Ordnerstruktur, SPM-Dependencies (Runestone, MarkdownUI), Build-Configs (Dev/Int/Prod), TestFlight Setup pending
id content status
phase-1 Phase 1: Core Networking Layer -- APIClient (URLSession + Cookies + Headers), SSEClient (POST-basiert, async bytes), CSRFManager pending
id content status
phase-2 Phase 2: Authentication -- LocalAuth, ASWebAuthenticationSession fuer MS/Google, Keychain, Biometrie, 401-Handler pending
id content status
phase-3 Phase 3: Domain Models (Mandate, Feature, Instance, Permissions, Pagination) + FeatureStore (@Observable) pending
id content status
phase-4 Phase 4: App Shell -- NavigationSplitView (iPad/iPhone adaptiv), Backend-driven Sidebar, Dashboard, SF Symbol Mapping pending
id content status
phase-5 Phase 5: i18n String Catalogs (de/en/fr) + Theme System (System/Light/Dark) + DesignTokens pending
id content status
phase-6 Phase 6: Shared UI -- FormGeneratorForm + FormGeneratorTable + Report, ContentPreview, ChatMessage, Toast, SearchBar pending
id content status
phase-7 Phase 7: Core Pages -- Store, GDPR, Basedata (Prompts/Files/Connections), Billing (read-only), Settings pending
id content status
phase-8 Phase 8: Admin Module -- 16 Admin-Seiten (Mandates, Users, RBAC, Invitations, Wizards, Billing Admin, Logs) pending
id content status
phase-9 Phase 9: Feature Trustee -- Dashboard, Documents, Positions, Roles, Expense-Import, VisionKit Scan, Accounting pending
id content status
phase-10 Phase 10: Feature Workspace -- SSE Chat-Streaming, Workflows, Files, Datasources, Pending Edits, KeepAlive pending
id content status
phase-11 Phase 11: Feature Chatbot -- SSE-Streaming Chat, Threads, Conversations pending
id content status
phase-12 Phase 12: Feature Teamsbot -- Sessions, WebSocket Bot, Config, MFA, Screenshots pending
id content status
phase-13 Phase 13: Feature CommCoach -- Coaching Sessions, Text-Streaming, Tasks, Badges, Scores, Export pending
id content status
phase-14 Phase 14: Feature Automation -- Definitions CRUD, Templates, Execute, Workflow-Management pending
id content status
phase-15 Phase 15: Feature Automation2 -- Workflows CRUD, Tasks, Execute (ohne visuellen Node-Editor) pending
id content status
phase-16 Phase 16: Feature CodeEditor -- Runestone Editor, SSE-Stream, Pending Edits, Syntax-Highlighting pending
id content status
phase-17 Phase 17: Feature RealEstate/PEK -- MapKit Integration, Parcels, Address-Search, BZO, Koordinaten-Transformation pending
id content status
phase-18 Phase 18: Feature Neutralization -- Config, Neutralize Text/File, Stats pending
false

Nyla iOS/iPadOS App -- Aktualisierter Implementierungsplan

Aenderungen gegenueber dem bestehenden Plan

Basierend auf der vollstaendigen Code-Analyse und den Klaerungen:

  • Korrektur Auth: Backend-Popup-Flow (window.open) wird durch ASWebAuthenticationSession ersetzt -- KEIN MSAL iOS SDK noetig, da das Backend die OAuth-Flows selbst handled
  • Entfernt: Voice-Features (Phase ueberall vereinfacht), Push Notifications (Phase 8 entfaellt), Automation2 visueller Node-Editor, Stripe Checkout
  • Hinzugefuegt: Detailliertes FormGenerator-Mapping, Workspace-Layout nach Apple HIG, nativer Code-Editor mit Runestone
  • Korrektur Billing: Nur read-only (Balance, Transaktionen, Statistiken) -- kein Checkout
  • iOS 18+: Erlaubt Nutzung aller modernen SwiftUI APIs
  • Team: 2-3 Entwickler, parallelisierbare Feature-Module

Architektur-Ueberblick

graph TD
    subgraph presentation [SwiftUI Views]
        Views[Screens und Components]
        FormGen[FormGenerator SwiftUI]
        ChatUI[Chat und Streaming UI]
    end

    subgraph viewmodels [ViewModels]
        VM["@Observable ViewModels"]
    end

    subgraph repositories [Repositories]
        Repo[Repository Protokolle]
    end

    subgraph networking [Networking Layer]
        APIClient[APIClient URLSession]
        SSEClient[SSEClient async bytes]
        CSRFMgr[CSRFManager]
        CookieStore[HTTPCookieStorage]
    end

    subgraph backend [Gateway FastAPI]
        API["/api/* Endpoints"]
    end

    Views --> VM
    FormGen --> VM
    ChatUI --> VM
    VM --> Repo
    Repo --> APIClient
    Repo --> SSEClient
    APIClient --> CSRFMgr
    APIClient --> CookieStore
    APIClient --> API
    SSEClient --> API

Technische Entscheidungen (aktualisiert)

  • Plattform: iOS 18+ / iPadOS 18+ (iPhone + iPad mit adaptivem Layout)
  • UI-Framework: SwiftUI (rein, kein UIKit ausser wo zwingend noetig)
  • Architektur: MVVM + Repository Pattern
  • Networking: URLSession + async/await + Codable
  • SSE: Custom Client auf URLSession.bytes(for:) Basis (POST-basiert, nicht EventSource)
  • Auth: ASWebAuthenticationSession fuer MS/Google OAuth, Local Login via API, Keychain fuer Token-Storage
  • State: @Observable (Observation Framework)
  • Navigation: NavigationSplitView (iPad 2-3 Spalten) + NavigationStack (iPhone auto-collapse)
  • DI: SwiftUI @Environment
  • Packages: SPM -- Runestone (Code-Editor), ggf. MarkdownUI
  • Karten: MapKit (SwiftUI)
  • Charts: Swift Charts Framework
  • i18n: String Catalogs (.xcstrings) fuer de/en/fr
  • Persistenz: Keychain (Secrets), UserDefaults (Preferences)
  • Distribution: TestFlight

Projektstruktur

NylaApp/
  NylaApp.swift
  Config/
    AppConfig.swift              # API URLs per Build Config
    Environment.swift            # Dev/Int/Prod
  Core/
    Networking/
      APIClient.swift            # Zentraler HTTP-Client (= api.ts)
      APIError.swift
      APIEndpoints.swift
      SSEClient.swift            # Server-Sent Events (= sseClient.ts)
      CSRFManager.swift          # CSRF Token (= csrfUtils.ts)
      RequestContext.swift       # X-Mandate-Id, X-Instance-Id
    Auth/
      AuthManager.swift          # Zentrale Auth-Logik
      LocalAuthService.swift     # Username/Password
      OAuthService.swift         # ASWebAuthenticationSession (MS + Google)
      KeychainService.swift
    Navigation/
      NavigationStore.swift      # Backend-driven Nav State
      AppRouter.swift            # Root Navigation Coordinator
    Localization/
      Localizable.xcstrings
      LanguageManager.swift
    Theme/
      ThemeManager.swift
      DesignTokens.swift
  Domain/
    Models/                      # Codable Structs
    Repositories/                # Protokolle
  Data/
    API/                         # 21 API-Module (= src/api/*.ts)
    Repositories/                # Implementierungen
  Features/                      # Feature-Module (je Ordner)
    Dashboard/
    Store/
    Settings/
    GDPR/
    Basedata/
    Billing/
    Admin/
    Trustee/
    Workspace/
    Chatbot/
    Teamsbot/
    CommCoach/
    Automation/
    Automation2/
    RealEstate/
    Neutralization/
  Shared/
    Components/
      FormGenerator/             # Dynamische Formulare
      ContentPreview/
      ChatMessage/
      AccessRules/
      SearchBar/
      LoadingView/
      ErrorView/
      EmptyStateView/
    Extensions/
    Utilities/
  Resources/
    Assets.xcassets

Phase 0: Projekt-Setup (1-2 Tage)

  • Xcode-Projekt erstellen (iOS 18+, SwiftUI App Lifecycle, iPhone + iPad)
  • Ordnerstruktur gemaess obigem Schema
  • SPM Dependencies:
    • Runestone (Code-Editor mit TreeSitter) -- fuer Phase 17
    • MarkdownUI (oder Apple's native AttributedString) -- fuer Chat-Rendering
    • Alle anderen Features nutzen System-Frameworks (MapKit, Charts, PDFKit)
  • Build-Konfigurationen (.xcconfig): Dev / Int / Prod
  • TestFlight: App ID, Provisioning Profile, Code Signing
  • SwiftLint Konfiguration
  • Imlementiere unter zuhilfenahme der rules unter /.cursor

Phase 1: Core Networking Layer (3-5 Tage)

APIClient.swift -- Equivalent zu frontend_nyla/src/api.ts

Zentrale Anforderungen aus der Web-Analyse:

  • Cookie-basierte Auth: URLSessionConfiguration.default mit httpCookieStorage = .shared -- httpOnly Cookies werden automatisch gesendet (withCredentials: true Equivalent)
  • Bearer Token: Aus Keychain lesen, als Authorization: Bearer {token} Header setzen (nur wenn vorhanden, analog Zeile ~100-110 in api.ts)
  • Kontext-Headers: X-Mandate-Id und X-Instance-Id aus dem aktuellen NavigationStore (analog getContextFromUrl() in api.ts das die URL parst -- in Swift kommt der Context aus dem Navigation State, nicht aus einer URL)
  • CSRF: Fuer POST/PUT/PATCH/DELETE automatisch X-CSRF-Token Header setzen
  • 401 Handler: Auth-State clearen und zur Login-Ansicht navigieren (analog api.ts Response Interceptor)
  • 429 Handler: Rate-Limit Warning anzeigen
  • Error Parsing: FastAPI detail kann String oder Array sein -- beide Faelle behandeln
// Konzept APIClient
@Observable class APIClient {
    func get<T: Decodable>(_ path: String, query: [String: String]?) async throws -> T
    func post<T: Decodable>(_ path: String, body: Encodable?) async throws -> T
    func put<T: Decodable>(_ path: String, body: Encodable?) async throws -> T
    func delete(_ path: String) async throws
    func upload<T: Decodable>(_ path: String, fileData: Data, fileName: String, fields: [String: String]?) async throws -> T
}

SSEClient.swift -- Equivalent zu frontend_nyla/src/utils/sseClient.ts

Kritisch: Das Web nutzt fetch(POST) mit ReadableStream -- NICHT standard EventSource (GET). In Swift:

func startStream(url: URL, body: Encodable, onEvent: @escaping (SSEEvent) -> Void) async throws {
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = try JSONEncoder().encode(body)
    // + Auth headers, CSRF, Content-Type

    let (bytes, response) = try await URLSession.shared.bytes(for: request)
    for try await line in bytes.lines {
        if line.hasPrefix("data: ") {
            let json = String(line.dropFirst(6))
            let event = try JSONDecoder().decode(SSEEvent.self, from: json.data(using: .utf8)!)
            onEvent(event)
        }
    }
}

Wird benoetigt fuer: Workspace Chat, Chatbot, CommCoach, Trustee Streaming.

CSRFManager.swift -- Equivalent zu frontend_nyla/src/utils/csrfUtils.ts

  • Token: 16 Hex-Zeichen via SecRandomCopyBytes (analog crypto.getRandomValues)
  • Gespeichert im Keychain (nicht UserDefaults -- Sicherheit)
  • Automatisch bei jedem mutierenden Request angehaengt

Phase 2: Authentication (3-5 Tage)

Kritische Erkenntnis: Zwei Auth-Patterns koexistieren

Die Web-App nutzt zwei verschiedene Auth-Mechanismen gleichzeitig:

  1. Local Login: POST /api/local/login (form-encoded) setzt httpOnly Cookies -- danach Cookie-basierte Auth
  2. Microsoft/Google: Backend-Popup-Flow setzt sowohl Cookies ALS AUCH localStorage.authToken + Bearer Header

Fuer die native App (ohne Backend-Aenderungen):

  • Local Login: Funktioniert direkt -- POST /api/local/login setzt Cookies, URLSession speichert sie automatisch
  • Microsoft OAuth: ASWebAuthenticationSession oeffnet /api/msft/auth/login im Safari-Sheet. Das Backend macht den OAuth-Dance und redirected am Ende zurueck. Der Callback-URL-Handler muss die Cookies aus dem Safari-Sheet in die URLSession uebernehmen. Herausforderung: ASWebAuthenticationSession teilt Cookies mit Safari, NICHT mit URLSession. Loesung: Nach dem OAuth-Flow GET /api/msft/me aufrufen -- wenn Cookies korrekt gesetzt sind, kommt der User zurueck. Falls nicht: prefersEphemeralWebBrowserSession = false verwenden, damit Cookies persistent sind.
  • Google OAuth: Gleicher Flow wie Microsoft via ASWebAuthenticationSession mit /api/google/auth/login

AuthManager.swift

@Observable class AuthManager {
    var isAuthenticated = false
    var currentUser: User?
    var authAuthority: String? // "local", "msft", "google"

    func loginLocal(username: String, password: String) async throws
    func loginMicrosoft(presentingWindow: ASPresentationAnchor) async throws
    func loginGoogle(presentingWindow: ASPresentationAnchor) async throws
    func fetchCurrentUser() async throws -> User
    func logout() async throws
}

Analog zu:

Login Screen

  • Username/Password Felder
  • "Mit Microsoft anmelden" Button -> ASWebAuthenticationSession
  • "Mit Google anmelden" Button -> ASWebAuthenticationSession
  • Face ID / Touch ID (wenn zuvor erfolgreich eingeloggt)
  • Links: Registrierung, Passwort vergessen
  • Analog zu frontend_nyla/src/pages/Login.tsx

Biometrische Auth (Bonus, nicht im Web)

  • Nach erstem Login: Frage ob Face ID/Touch ID aktiviert werden soll
  • Credentials verschluesselt im Keychain speichern
  • Bei App-Start: Biometrie -> Auto-Login

Phase 3: Domain Models + Feature Store (2-3 Tage)

Zentrale Models

Mapping der TypeScript-Types aus frontend_nyla/src/types/mandate.ts:

struct I18nLabel: Codable {
    var de: String; var en: String; var fr: String?
    func localized(_ lang: String) -> String {
        switch lang {
        case "en": return en
        case "fr": return fr ?? de
        default: return de
        }
    }
}

enum AccessLevel: String, Codable { case none = "n", my = "m", group = "g", all = "a" }

struct TablePermission: Codable {
    var view: Bool
    var read, create, update, delete: AccessLevel
}

struct FieldPermission: Codable { var read: Bool; var write: Bool }

struct InstancePermissions: Codable {
    var tables: [String: TablePermission]
    var fields: [String: [String: FieldPermission]]?
    var views: [String: Bool]
    var isAdmin: Bool?
}

struct FeatureInstance: Codable, Identifiable {
    var id: String
    var featureCode, mandateId, mandateName, instanceLabel: String
    var userRoles: [String]
    var permissions: InstancePermissions
}

struct MandateFeature: Codable {
    var code: String; var label: I18nLabel; var icon: String
    var instances: [FeatureInstance]
}

struct Mandate: Codable, Identifiable {
    var id, name: String
    var label, code: String?
    var features: [MandateFeature]
}

FeatureStore -- Analog zu frontend_nyla/src/stores/featureStore.tsx

@Observable class FeatureStore {
    var mandates: [Mandate] = []
    var isLoading = false
    var isInitialized = false

    func loadFeatures() async throws  // GET /api/features/my
    func getMandateById(_ id: String) -> Mandate?
    func getInstanceById(_ id: String) -> FeatureInstance?
}

Pagination Model -- Analog zum Backend PaginatedResponse

struct PaginatedResponse<T: Codable>: Codable {
    var items: [T]
    var total: Int
    var page: Int
    var pageSize: Int
    var totalPages: Int
}

struct PaginationParams: Encodable {
    var page: Int = 1
    var pageSize: Int = 25
    var search: String?
    var sort: [SortConfig]?
    var filters: [String: String]?
}

Phase 4: App Shell + Navigation (4-6 Tage)

Adaptive Layout nach Apple HIG

iPad (Regular Width):

graph LR
    subgraph splitView [NavigationSplitView]
        Sidebar[Sidebar: Navigation]
        Content[Content Area]
    end
    Sidebar --> Content
  • NavigationSplitView mit Sidebar + Detail
  • Sidebar: Backend-driven Navigation (Mandate > Feature > Instance > Views)
  • Detail: NavigationStack fuer den Content-Bereich

iPhone (Compact Width):

  • Automatisches Collapse durch NavigationSplitView in NavigationStack
  • Tab-basierte Hauptnavigation fuer schnellen Zugriff auf Dashboard, Settings
  • Sidebar oeffnet sich als Sheet oder wird zum NavigationStack

Backend-driven Navigation

GET /api/navigation?language={lang} liefert den kompletten Navigationsbaum.

Analog zu frontend_nyla/src/hooks/useNavigation.ts und frontend_nyla/src/components/Navigation/MandateNavigation.tsx:

  • Static Blocks: "Meine Sicht" (Dashboard, Store, Settings, etc.), "Administration" (Admin-Seiten mit Subgroups)
  • Dynamic Block: Mandate > Features > Instances > Views (hierarchischer Baum)
  • Icon-Mapping: Web react-icons -> SF Symbols (Mapping-Tabelle erstellen)

Screen-Routing

enum AppDestination: Hashable {
    case dashboard
    case store
    case settings
    case gdpr
    case basedata(BasedataSection)
    case billing
    case admin(AdminSection)
    case feature(mandateId: String, featureCode: String, instanceId: String, view: String)
}

Analog zum Route-Setup in frontend_nyla/src/App.tsx -- der VIEW_COMPONENTS Map in frontend_nyla/src/pages/FeatureView.tsx.

Feature-View-Dispatcher

@ViewBuilder
func featureView(code: String, view: String, instance: FeatureInstance) -> some View {
    switch (code, view) {
    case ("trustee", "dashboard"): TrusteeDashboardView(instance: instance)
    case ("trustee", "documents"): TrusteeDocumentsView(instance: instance)
    case ("workspace", "dashboard"): WorkspaceView(instance: instance)
    case ("chatbot", "conversations"): ChatbotConversationsView(instance: instance)
    // ... alle Mappings
    default: NotFoundView()
    }
}

MainLayout-Equivalent

Analog zu frontend_nyla/src/layouts/MainLayout.tsx:

  • Logo oben in der Sidebar
  • MandateNavigation als Hauptinhalt der Sidebar
  • UserSection am unteren Rand der Sidebar (Profilbild, Name, Logout)
  • FeatureLayout: Breadcrumb Header (Mandate > Feature > Instance) + Role Badge, analog frontend_nyla/src/layouts/FeatureLayout.tsx

Dashboard

Analog zu frontend_nyla/src/pages/Dashboard.tsx:

  • Titel "Uebersicht" mit Anzahl Instanzen/Mandate
  • Sektionen pro Mandate mit Instance-Karten
  • Jede Karte linkt zur ersten View der Instanz (instance.views[0].uiPath)
  • Bei 0 Instanzen: Navigation zum Store

Phase 5: i18n + Theme (2-3 Tage)

Internationalisierung

  • String Catalogs (.xcstrings) fuer de/en/fr
  • Alle statischen Strings aus frontend_nyla/src/locales/de.ts, en.ts, fr.ts uebernehmen
  • Dynamische Labels: I18nLabel.localized(lang) Helper
  • LanguageManager speichert Praeferenz in UserDefaults, default de
  • Sprachauswahl in Settings analog Web

Theme

  • @AppStorage("theme") var theme: String = "system"
  • .preferredColorScheme() fuer System-Integration (auto/light/dark)
  • DesignTokens: Farben als Color Extensions, Spacing als CGFloat Constants
  • CSS-Variable-Equivalent: SwiftUI @Environment(\.colorScheme) + Custom EnvironmentValues
  • Mapping der CSS-Variablen aus dem Web (--primary-color, --border-color, etc.) zu Swift Color Assets

Phase 6: Shared UI Components (5-8 Tage)

FormGenerator (KRITISCH -- wird von fast allen Features genutzt)

Analog zu frontend_nyla/src/components/FormGenerator/:

Konzept: Dynamische Formulare und Tabellen basierend auf AttributeDefinition Arrays vom Backend (GET /api/attributes/{entityType}).

Attribut-Typ-Mapping (Web -> Swift)

Basierend auf frontend_nyla/src/utils/attributeTypeMapper.ts:

  • text / string -> TextField
  • textarea -> TextEditor (mit minRows/maxRows -> .frame(minHeight:maxHeight:))
  • email -> TextField mit .keyboardType(.emailAddress)
  • url -> TextField mit .keyboardType(.URL)
  • password -> SecureField
  • number / integer / float -> TextField mit .keyboardType(.decimalPad) + NumberFormatter
  • select / enum -> Picker (Wheel/Menu/Inline je nach Kontext)
  • multiselect -> Multi-Selection List oder Chip-basierte Auswahl
  • checkbox / boolean -> Toggle
  • date -> DatePicker(.date)
  • time -> DatePicker(.hourAndMinute)
  • timestamp -> DatePicker (date + time)
  • multilingual -> Custom View mit 3 TextFields (de/en/fr) und Sprach-Tabs
  • file -> File-Picker Button + Vorschau
  • readonly -> Text (nicht editierbar)

FormGeneratorForm (Formular-Ansicht)

struct FormGeneratorForm: View {
    let entityType: String
    let mode: FormMode  // .create, .edit, .view
    @Binding var data: [String: AnyCodable]
    var attributes: [AttributeDefinition]?  // optional, sonst von API laden
    var onSubmit: (([String: AnyCodable]) async throws -> Void)?
    var filterFields: [String]?
    var customValidator: (([String: AnyCodable], [AttributeDefinition]) -> [String: String])?
}
  • Laedt Attribute von GET /api/attributes/{entityType} wenn nicht uebergeben
  • Sortiert nach order (default 999)
  • Filtert nach visible, editableOnCreate/editableOnUpdate
  • Options-Loading: Wenn options ein String ist (API-Pfad), parallele GETs mit {instanceId} Replacement

FormGeneratorTable (Tabellen-Ansicht)

Analog zu frontend_nyla/src/components/FormGenerator/FormGeneratorTable/FormGeneratorTable.tsx:

  • Server-seitige Pagination, Sortierung, Filterung (kein Client-Side!)
  • Debounced Suche (300ms) -> paginationParams.search
  • Multi-Column Sort (asc -> desc -> entfernt)
  • Filter-Dropdowns pro Spalte (Werte von {apiEndpoint}/filter-values?column=...)
  • Pagination Controls: First/Prev/Next/Last + Seitenzahlen
  • Row Actions: Edit, Delete, Download, Custom Actions
  • Inline Editing fuer unterstuetzte Typen
  • Group-By Support mit Custom Group Renderer

Auf iOS/iPad: List oder LazyVStack statt HTML-Table. Auf iPad breites Layout, auf iPhone Card-basiert oder horizontales Scrollen.

FormGeneratorReport (Statistik-Ansicht)

  • Sektionen: KPI Grid, Bar, Line, Area, Pie Charts, Tabellen
  • Toolbar: Periodenfilter, Datumsbereich
  • Swift Charts fuer alle Chart-Typen (Recharts-Equivalent)

ContentPreview

  • PDF: PDFKitView (UIViewRepresentable mit PDFKit)
  • Bilder: AsyncImage
  • JSON: Syntax-Highlighting
  • HTML: WKWebView (Mini)

ChatMessage Components

Analog zu frontend_nyla/src/components/UiComponents/Messages/:

  • User vs. Assistant Bubbles
  • Markdown-Rendering (MarkdownUI oder AttributedString)
  • Code-Blocks mit Syntax-Highlighting
  • File-Attachments (Karten mit Icon + Name)
  • Streaming-Indicator (Typing Animation)
  • Tool-Activity Anzeige
  • Auto-Scroll zum neuesten Eintrag

Weitere Shared Components

  • SearchBar: Debounced Suchfeld
  • LoadingView: Spinner/Skeleton
  • ErrorView: Fehlermeldung mit Retry-Button
  • EmptyStateView: Illustration + Text + Action
  • Toast: Analog frontend_nyla/src/contexts/ToastContext.tsx -- Types: success (5s), error (8s), warning (6s), info (5s)

Phase 7: Core Pages (5-7 Tage)

Store (Feature Marketplace)

GDPR

Basedata -- Prompts

Basedata -- Files

  • GET /api/files/list, Upload, Download, Preview
  • Ordnerstruktur: GET /api/files/folders
  • Upload: UIDocumentPickerViewController (via UIViewControllerRepresentable)
  • Vorschau: QuickLook Framework
  • Batch-Delete, Move, Folder-Management
  • Analog frontend_nyla/src/pages/FilesPage.tsx

Basedata -- Connections

Billing (Read-Only)

Settings

  • Theme-Toggle (System/Light/Dark)
  • Sprachauswahl (de/en/fr)
  • Profil-Bearbeitung (via /api/users/{userId})
  • Passwort aendern (POST /api/users/change-password)
  • Analog frontend_nyla/src/pages/Settings.tsx

Phase 8: Admin Module (5-7 Tage)

Alle Admin-Seiten nutzen den FormGenerator intensiv. Analog zu frontend_nyla/src/pages/admin/:

  • Mandates: CRUD /api/mandates/ + User-Zuweisung
  • Users: CRUD /api/users/ + Password-Link senden
  • User-Mandates: /api/mandates/{id}/users -- Zuweisungen verwalten
  • Access Hub: /api/rbac/permissions, /api/rbac/rules -- Regel-Editor
  • Feature Instances: /api/features/instances -- CRUD + Sync-Roles
  • Feature Roles: /api/features/templates/roles -- Template-Rollen
  • Feature Users: /api/features/instances/{id}/users -- Benutzer pro Instanz
  • Invitations: CRUD /api/invitations/ + Token-Validierung
  • Mandate Roles: /api/rbac/roles -- Rollen-Verwaltung
  • Role Permissions: /api/rbac/rules/by-role/{roleId} -- Matrix-Ansicht
  • User Access Overview: /api/admin/user-access-overview/ -- Read-Only Uebersicht
  • Billing Admin: /api/billing/admin/ -- Read-Only (Accounts, Transactions, Settings)
  • Subscriptions Admin: /api/subscription/admin/all -- Uebersicht
  • Automation Events: /api/admin/automation-events -- Liste + Sync
  • Logs: /api/admin/logs -- Systemlogs mit Download
  • Mandate Wizard: Kombination von Mandate erstellen + Features zuweisen + Benutzer einladen
  • Invitation Wizard: Gesteuerter Einladungs-Flow

Phase 9: Feature Trustee (5-7 Tage)

API-Basis: /api/trustee/{instanceId}/

Views (aus frontend_nyla/src/pages/views/trustee/):

  • Dashboard: Uebersicht ueber Organisationen, Vertraege, Positionen
  • Documents: CRUD + Upload (POST .../documents/upload) + Download
  • Positions: CRUD mit verschachtelten Position-Documents
  • Instance-Roles: Rollenverwaltung pro Instanz
  • Expense-Import: CSV/Excel Import von Belegen (Automation-basiert)
  • Scan-Upload: Dokument-Scan -> auf iOS: VNDocumentCameraViewController (VisionKit) fuer native Scan-Funktion (besser als Web!)
  • Accounting Settings: Connector-Konfiguration, Sync, Chart of Accounts

Besonderheiten:

  • Viele Options-Endpoints fuer Dropdowns (.../organisations/options, .../roles/options, etc.)
  • Hierarchische Daten: Organisation > Contract > Document > Position
  • Scan-Upload ist auf iOS BESSER als im Web dank VisionKit

Phase 10: Feature Workspace (5-7 Tage)

API-Basis: /api/workspace/{instanceId}/

Haupt-View: AI Chat mit SSE-Streaming

Workspace Layout (Apple HIG Best Practice)

iPad (Regular Width) -- NavigationSplitView mit 3 Spalten:

  • Sidebar: Conversations/Workflows Liste (analog linke Spalte im Web)
  • Content: Chat-Bereich mit Nachrichten + Input-Feld
  • Detail (Inspector): Activity Panel, File Preview, Pending Edits (als .inspector Modifier oder dritte Spalte)

iPhone (Compact Width) -- Automatischer Collapse:

  • Chat ist Primary View
  • Conversations-Liste als Back-Navigation
  • Activity/Files als Toolbar-Button -> Sheet/Overlay

SSE Chat-Streaming

Analog zu frontend_nyla/src/pages/views/workspace/useWorkspace.ts:

  • POST /api/workspace/{instanceId}/start/stream mit JSON Body:
    • prompt, fileIds, dataSourceIds, featureDataSourceIds, userLanguage, workflowId, allowedProviders
  • Event-Types aus dem Stream: message, chunk, status, tool_activity, agent_progress, agent_summary, fileCreated, dataSourceAccess, workflowUpdated, complete, stopped, error
  • Stop: POST .../stop/{workflowId}

Weitere Workspace-Features (ohne Voice)

  • Workflows CRUD: List, Create, Patch, Delete
  • Messages: GET .../workflows/{id}/messages
  • Files + Folders: Browse, Upload, Preview
  • Datasources: CRUD + Feature-Datasources
  • Pending Edits: Accept/Reject (einzeln und alle)
  • Settings: General + RAG-Statistiken

WorkspaceKeepAlive-Equivalent

Im Web bleibt die Workspace-Komponente gemounted (analog frontend_nyla/src/pages/views/workspace/WorkspaceKeepAlive.tsx).

In Swift: @Observable WorkspaceViewModel wird im Environment gehalten und ueberlebt Navigation-Wechsel. Der SSE-Stream bleibt aktiv solange die Instanz ausgewaehlt ist.


Phase 11: Feature Chatbot (3-5 Tage)

API-Basis: /api/chatbot/{instanceId}/

Views:

  • Conversations: Thread-Liste + Chat-Stream
  • Settings: Placeholder im Web, aber Endpoints existieren

Technisch identisch zum Workspace-Chat-Pattern:

  • POST .../start/stream -> SSE
  • POST .../stop/{workflowId}
  • Threads: List, Delete
  • Verwendet dieselben ChatMessage-Components aus Phase 6

Phase 12: Feature Teamsbot (3-5 Tage, OHNE Voice)

API-Basis: /api/teamsbot/{instanceId}/

Views:

  • Dashboard: Uebersicht Sessions + Config
  • Sessions: Session-Liste, Session-Details mit Stream
  • Settings: Config, System Bots, User Account

Technisch:

  • SSE fuer Session-Stream (GET .../sessions/{sessionId}/stream)
  • Config/Settings CRUD
  • Screenshots anzeigen (GET .../sessions/{sessionId}/screenshots)
  • WebSocket (/bot/ws/{sessionId}) fuer Live-Bot-Interaktion -- URLSessionWebSocketTask
  • MFA-Support fuer Session-Authentifizierung

Phase 13: Feature CommCoach (4-6 Tage, OHNE Audio-Streaming)

API-Basis: /api/commcoach/{instanceId}/

Views:

  • Dashboard: Kontext-Uebersicht, aktive Sessions, Fortschritt
  • Coaching: Session starten, Nachrichten-Stream (SSE), Tasks
  • Dossier: Export, Score-History, Badges
  • Settings: Personas, Documents

Technisch:

  • Contexts CRUD (Create, Archive, Activate)
  • Sessions: Start, Complete, Cancel
  • Message-Stream: SSE fuer Coaching-Nachrichten
  • Tasks: CRUD + Status-Updates
  • Badges + Scores: Visualisierung mit Swift Charts
  • Export als PDF/Download

Hinweis: Audio-Streaming (Mikrofon -> Backend) wird in dieser Version uebersprungen. Text-basiertes Coaching ist voll funktional.


Phase 14: Feature Automation (3-5 Tage)

API-Basis: /api/automations/

Views:

  • Definitions: Automation-Definitionen CRUD + Execute + Duplicate
  • Templates: Template-Verwaltung

Technisch:

  • FormGeneratorTable fuer Definitionen/Templates
  • Execute mit Status-Tracking
  • Workflow-Management: List, Get, Status, Logs, Messages
  • Actions-Endpoint fuer verfuegbare Aktionen

Phase 15: Feature Automation2 (2-3 Tage, OHNE visuellen Editor)

API-Basis: /api/automation2/{instanceId}/

Views:

  • Workflows: Liste aller Workflows + CRUD
  • Workflow-Tasks: Offene Tasks mit Complete-Aktion

Kein visueller Node-Editor -- nur Listen-basierte Verwaltung:

  • Workflows: List, Create, Update, Delete
  • Workflow Runs: GET .../workflows/{id}/runs
  • Execute: POST .../execute
  • Tasks: List + Complete
  • Node-Types und Connections als Reference-Info

Phase 16: Feature CodeEditor (3-5 Tage)

API-Basis: Nutzt Workspace/Automation2 API-Patterns

Views:

  • Editor: Code-Anzeige mit Syntax-Highlighting (Runestone)
  • Workflows: Workflow-Liste

Technisch:

  • Runestone (SPM Package) fuer nativen Code-Editor mit:
    • TreeSitter-basiertes Syntax-Highlighting
    • Zeilennummern
    • Theme-Integration (Light/Dark)
  • SSE-Stream fuer Code-Generierung
  • Pending Edits: Accept/Reject wie im Workspace
  • File-Content anzeigen: GET .../files/{fileId}/content

Phase 17: Feature RealEstate/PEK (5-7 Tage)

API-Basis: /api/realestate/{instanceId}/

Views:

  • Dashboard (Map): Karten-Visualisierung mit Parzellen
  • Instance-Roles: Rollen-Verwaltung

Technisch:

  • MapKit (SwiftUI Map View):
    • Parzellen als Polygone auf der Karte
    • Parcel-Selection durch Tap
    • Adjacent Parcels Highlight
    • Cluster-Ansicht fuer viele Parzellen
  • Address-Autocomplete: GET /api/realestate/address/autocomplete + optionales MKLocalSearchCompleter
  • Projects + Parcels CRUD
  • BZO Information: Bauvorschriften anzeigen
  • WFS (Web Feature Service): Parcel-Geometrie laden
  • Selection Summary

Hinweis: Leaflet (Web) -> MapKit (iOS) erfordert Koordinaten-Transformation. proj4 im Web wird durch MapKit's native Projektionen ersetzt. Falls Swiss-spezifische Koordinatensysteme (LV95/LV03) benoetigt werden, braucht es einen Converter.


Phase 18: Feature Neutralization (2-3 Tage)

API-Basis: /api/neutralization/

Views:

  • Dashboard/Playground (gleiche View): Text eingeben -> neutralisieren/aufloesen

Technisch:

  • Config: GET/POST /api/neutralization/config
  • Neutralize Text: POST .../neutralize-text
  • Resolve Text: POST .../resolve-text
  • Neutralize File: POST .../neutralize-file (File-Upload)
  • Stats: GET .../stats
  • Attributes: GET .../attributes

API-Header-Konvention (fuer alle Requests)

Jeder Request muss folgende Header senden (analog frontend_nyla/src/api.ts):

  • Authorization: Bearer {token} -- aus Keychain, wenn JWT vorhanden
  • X-Mandate-Id: {mandateId} -- aus NavigationStore, bei Feature-Seiten
  • X-Instance-Id: {instanceId} -- aus NavigationStore, bei Feature-Seiten
  • X-CSRF-Token: {token} -- aus CSRFManager, bei POST/PUT/PATCH/DELETE
  • Content-Type: application/json -- Standard fuer JSON Bodies
  • Cookies (httpOnly) -- automatisch via URLSession HTTPCookieStorage

Parallelisierungsstrategie (2-3 Entwickler)

gantt
    title Entwicklungsplan 2-3 Entwickler
    dateFormat YYYY-MM-DD
    axisFormat %d.%m

    section Gemeinsam
    Phase0_Setup           :p0, 2026-04-01, 2d
    Phase1_Networking      :p1, after p0, 5d
    Phase2_Auth            :p2, after p1, 5d
    Phase3_Models          :p3, after p2, 3d

    section Dev1_Core
    Phase4_AppShell        :p4, after p3, 6d
    Phase5_i18n_Theme      :p5, after p4, 3d
    Phase7_CorePages       :p7, after p5, 7d
    Phase8_Admin           :p8, after p7, 7d

    section Dev2_Components
    Phase6_SharedUI        :p6, after p3, 8d
    Phase10_Workspace      :p10, after p6, 7d
    Phase11_Chatbot        :p11, after p10, 5d
    Phase13_CommCoach      :p13, after p11, 6d

    section Dev3_Features
    Phase9_Trustee         :p9, after p6, 7d
    Phase12_Teamsbot       :p12, after p9, 5d
    Phase14_Automation     :p14, after p12, 5d
    Phase15_Automation2    :p15, after p14, 3d
    Phase16_CodeEditor     :p16, after p15, 5d
    Phase17_RealEstate     :p17, after p16, 7d
    Phase18_Neutralization :p18, after p17, 3d
  • Phase 0-3: Gemeinsam (Basis fuer alle)
  • Ab Phase 4: Parallel -- Core/Admin, Shared UI/Chat-Features, Trustee/Weitere Features
  • Features sind nach Phase 6 (Shared Components, besonders FormGenerator) unabhaengig voneinander

Gesamtaufwand-Schaetzung (aktualisiert)

  • Phase 0: Setup -- 1-2 Tage
  • Phase 1: Networking -- 3-5 Tage
  • Phase 2: Authentication -- 3-5 Tage
  • Phase 3: Domain Models -- 2-3 Tage
  • Phase 4: App Shell + Navigation -- 4-6 Tage
  • Phase 5: i18n + Theme -- 2-3 Tage
  • Phase 6: Shared UI Components -- 5-8 Tage
  • Phase 7: Core Pages -- 5-7 Tage
  • Phase 8: Admin -- 5-7 Tage
  • Phase 9: Trustee -- 5-7 Tage
  • Phase 10: Workspace -- 5-7 Tage
  • Phase 11: Chatbot -- 3-5 Tage
  • Phase 12: Teamsbot -- 3-5 Tage
  • Phase 13: CommCoach -- 4-6 Tage
  • Phase 14: Automation -- 3-5 Tage
  • Phase 15: Automation2 -- 2-3 Tage
  • Phase 16: CodeEditor -- 3-5 Tage
  • Phase 17: RealEstate -- 5-7 Tage
  • Phase 18: Neutralization -- 2-3 Tage
  • Gesamt sequentiell: ~65-100 Tage
  • Gesamt mit 3 Devs parallel: ~35-50 Tage (nach Phase 3)

Risiken und offene Punkte

  1. Auth ohne Backend-Aenderungen: ASWebAuthenticationSession teilt Cookies mit Safari, nicht direkt mit URLSession. Falls das Backend nach dem OAuth-Redirect nur httpOnly Cookies setzt (ohne Token im Redirect-URL), muss getestet werden ob die Cookie-Uebernahme funktioniert. Worst case: Backend muss doch einen Token-Parameter im Callback-URL zurueckgeben.
  2. CSRF Validierung: Das Backend validiert CSRF-Tokens mit einem Hex-Format-Check. Die client-seitige Generierung muss identisch sein (16 Hex-Zeichen). Testen ob das Backend Session-gebundene CSRF-Validierung macht oder nur Format-Check.
  3. SSE ueber POST: Standard EventSource ist GET-only. Das Web nutzt fetch(POST) + ReadableStream. In Swift: URLSession.bytes(for:) mit POST Request -- funktioniert, aber muss getestet werden mit dem spezifischen Backend-Setup.
  4. FormGenerator Komplexitaet: Der FormGenerator ist das komplexeste Shared Component. Die Tabellen-Ansicht auf iPhone (schmaler Screen) braucht ein alternatives Layout (Cards statt Tabelle). Dies erfordert sorgfaeltiges Responsive Design.
  5. MapKit vs Leaflet: Falls Swiss-spezifische Koordinatensysteme (LV95/LV03) benoetigt werden, muss ein nativer Koordinaten-Transformer implementiert werden (im Web: proj4).
  6. Keine Offline-Faehigkeit: Bei schlechter Netzwerkverbindung (z.B. auf dem Bau) koennen Ladezustaende frustrierend sein. Empfehlung: Zumindest ein Request-Cache fuer die letzte erfolgreiche Response pro Endpoint.
  7. TestFlight Limitierung: Max 10'000 Tester, 90-Tage Build-Ablauf. Fuer laengerfristigen Einsatz Enterprise Distribution oder App Store evaluieren.