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). |
|
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
ASWebAuthenticationSessionersetzt -- 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 / ProdAPI_BASE_URL:http://localhost:8000/ INT-URL / PROD-URL- Analog zu frontend_nyla/config/config.ts (
VITE_API_BASE_URL)
- 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.defaultmithttpCookieStorage = .shared-- httpOnly Cookies werden automatisch gesendet (withCredentials: trueEquivalent) - Bearer Token: Aus Keychain lesen, als
Authorization: Bearer {token}Header setzen (nur wenn vorhanden, analog Zeile ~100-110 inapi.ts) - Kontext-Headers:
X-Mandate-IdundX-Instance-Idaus dem aktuellen NavigationStore (analoggetContextFromUrl()inapi.tsdas 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-TokenHeader setzen - 401 Handler: Auth-State clearen und zur Login-Ansicht navigieren (analog
api.tsResponse Interceptor) - 429 Handler: Rate-Limit Warning anzeigen
- Error Parsing: FastAPI
detailkann 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(analogcrypto.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:
- Local Login:
POST /api/local/login(form-encoded) setzt httpOnly Cookies -- danach Cookie-basierte Auth - 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/loginsetzt Cookies,URLSessionspeichert sie automatisch - Microsoft OAuth:
ASWebAuthenticationSessionoeffnet/api/msft/auth/loginim 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-FlowGET /api/msft/meaufrufen -- wenn Cookies korrekt gesetzt sind, kommt der User zurueck. Falls nicht:prefersEphemeralWebBrowserSession = falseverwenden, damit Cookies persistent sind. - Google OAuth: Gleicher Flow wie Microsoft via
ASWebAuthenticationSessionmit/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:
- frontend_nyla/src/hooks/useAuthentication.ts --
useAuth,useMsalAuth,useGoogleAuth - frontend_nyla/src/api/authApi.ts --
loginApi,fetchCurrentUserApi,logoutApi
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
NavigationSplitViewmit Sidebar + Detail- Sidebar: Backend-driven Navigation (Mandate > Feature > Instance > Views)
- Detail: NavigationStack fuer den Content-Bereich
iPhone (Compact Width):
- Automatisches Collapse durch
NavigationSplitViewinNavigationStack - 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.tsuebernehmen - Dynamische Labels:
I18nLabel.localized(lang)Helper LanguageManagerspeichert Praeferenz in UserDefaults, defaultde- Sprachauswahl in Settings analog Web
Theme
@AppStorage("theme") var theme: String = "system".preferredColorScheme()fuer System-Integration (auto/light/dark)DesignTokens: Farben alsColorExtensions, 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 SwiftColorAssets
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->TextFieldtextarea->TextEditor(mitminRows/maxRows->.frame(minHeight:maxHeight:))email->TextFieldmit.keyboardType(.emailAddress)url->TextFieldmit.keyboardType(.URL)password->SecureFieldnumber/integer/float->TextFieldmit.keyboardType(.decimalPad)+ NumberFormatterselect/enum->Picker(Wheel/Menu/Inline je nach Kontext)multiselect-> Multi-Selection List oder Chip-basierte Auswahlcheckbox/boolean->Toggledate->DatePicker(.date)time->DatePicker(.hourAndMinute)timestamp->DatePicker(date + time)multilingual-> Custom View mit 3 TextFields (de/en/fr) und Sprach-Tabsfile-> File-Picker Button + Vorschaureadonly->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
optionsein 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)
GET /api/store/features-> Feature-Liste als KartenPOST /api/store/activate/deactivate- Analog frontend_nyla/src/pages/Store.tsx
GDPR
GET /api/user/me/data-export,/data-portabilityDELETE /api/user/me/(mit Bestaetigung)- Analog frontend_nyla/src/pages/GDPR.tsx
Basedata -- Prompts
- FormGeneratorTable + FormGeneratorForm fuer CRUD auf
/api/prompts - Analog frontend_nyla/src/pages/PromptsPage.tsx
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
- FormGeneratorTable + FormGeneratorForm fuer CRUD auf
/api/connections/ - Connect/Disconnect Aktionen
- Analog frontend_nyla/src/pages/ConnectionsPage.tsx
Billing (Read-Only)
GET /api/billing/balance-> Saldo-AnzeigeGET /api/billing/transactions-> TransaktionslisteGET /api/billing/statistics/{period}-> Charts mit Swift Charts- KEIN Checkout/Stripe in der App
- Analog frontend_nyla/src/pages/billing/BillingDataView.tsx
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
.inspectorModifier 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/streammit 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-> SSEPOST .../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
MapView):- 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 vorhandenX-Mandate-Id: {mandateId}-- aus NavigationStore, bei Feature-SeitenX-Instance-Id: {instanceId}-- aus NavigationStore, bei Feature-SeitenX-CSRF-Token: {token}-- aus CSRFManager, bei POST/PUT/PATCH/DELETEContent-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
- Auth ohne Backend-Aenderungen:
ASWebAuthenticationSessionteilt Cookies mit Safari, nicht direkt mitURLSession. 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. - 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.
- SSE ueber POST: Standard
EventSourceist GET-only. Das Web nutztfetch(POST)+ReadableStream. In Swift:URLSession.bytes(for:)mit POST Request -- funktioniert, aber muss getestet werden mit dem spezifischen Backend-Setup. - 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.
- MapKit vs Leaflet: Falls Swiss-spezifische Koordinatensysteme (LV95/LV03) benoetigt werden, muss ein nativer Koordinaten-Transformer implementiert werden (im Web:
proj4). - 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.
- TestFlight Limitierung: Max 10'000 Tester, 90-Tage Build-Ablauf. Fuer laengerfristigen Einsatz Enterprise Distribution oder App Store evaluieren.