Files
Yuvomi-App/ios/Sources/NativeContracts.swift
Yuvomi Mobile Contributors 97570b7fdf feat(ios): replace embedded website with initial native SwiftUI API client
Introduce real SwiftUI auth/dashboard/task/note/shopping screens, a bounded origin-bound URLSession transport and Keychain-backed native sessions. Archive the rejected WebKit renderer rather than embedding it for unfinished modules. Sixteen portable URL/contract/transport tests and Swift format/parsing pass on Linux; Apple SDK application and UI validation remains mandatory. This is an incomplete native slice, not full PWA parity or a release.
2026-10-21 08:57:31 +02:00

148 lines
4.2 KiB
Swift

import Foundation
enum NativePage: String, CaseIterable, Identifiable, Sendable {
case home, tasks, shopping, notes, more
var id: String { rawValue }
var title: String {
switch self {
case .home: "Übersicht"
case .tasks: "Aufgaben"
case .shopping: "Einkauf"
case .notes: "Notizen"
case .more: "Mehr"
}
}
var symbol: String {
switch self {
case .home: "house"
case .tasks: "checkmark.square"
case .shopping: "cart"
case .notes: "note.text"
case .more: "ellipsis"
}
}
}
struct NativeBootstrap: Decodable, Sendable {
let appName: String
let setupRequired: Bool
let passwordResetEnabled: Bool
}
struct NativeAuthentication: Decodable, Sendable {
let enabled: Bool
let passwordLoginEnabled: Bool
let guestPasswordLoginEnabled: Bool?
}
struct NativeUser: Decodable, Sendable {
let id: Int
let username: String
let displayName: String
}
struct NativePermissions: Decodable, Sendable {
let admin: Bool
let modules: [String: String]
let widgets: [String: String]
func access(_ module: String) -> String { admin ? "write" : modules[module] ?? "write" }
func showsWidget(_ widget: String) -> Bool { admin || widgets[widget] != "none" }
}
struct NativeIdentity: Decodable, Sendable {
let user: NativeUser?
let permissions: NativePermissions?
let csrfToken: String?
let twoFactorRequired: Bool?
}
struct NativeEnvelope<Value: Decodable & Sendable>: Decodable, Sendable { let data: Value }
struct NativeEmpty: Decodable, Sendable {}
struct NativeTask: Decodable, Identifiable, Sendable {
let id: Int
let title: String
let description: String?
let status: String
let priority: String
let dueDate: String?
let category: String?
}
struct NativeNote: Decodable, Identifiable, Sendable {
let id: Int
let title: String?
let content: String
let pinned: Int
}
struct NativeShoppingList: Decodable, Identifiable, Sendable {
let id: Int
let name: String
let itemTotal: Int?
let itemChecked: Int?
let openCount: Int?
}
struct NativeShoppingItem: Decodable, Identifiable, Sendable {
let id: Int
let name: String
let quantity: String?
let category: String?
let isChecked: Int
}
struct NativeEventPreview: Decodable, Identifiable, Sendable {
let id: Int
let title: String
let startDatetime: String
}
struct NativeTaskPreview: Decodable, Identifiable, Sendable {
let id: Int
let title: String
let dueDate: String?
}
struct NativeDashboard: Decodable, Sendable {
let openTaskCount: Int?
let shoppingOpenCount: Int?
let urgentTasks: [NativeTaskPreview]
let upcomingEvents: [NativeEventPreview]
let shoppingLists: [NativeShoppingList]
let pinnedNotes: [NativeNote]
}
enum NativeValue: Encodable, Sendable {
case string(String)
case number(Int)
case bool(Bool)
case null
func encode(to encoder: Encoder) throws {
var value = encoder.singleValueContainer()
switch self {
case .string(let text): try value.encode(text)
case .number(let number): try value.encode(number)
case .bool(let flag): try value.encode(flag)
case .null: try value.encodeNil()
}
}
}
enum NativeAPIError: LocalizedError {
case response(Int)
case invalidResponse, invalidPath, unavailable, closed
var errorDescription: String? {
switch self {
case .response(401): "Anmeldung oder Sitzung ist nicht mehr gültig."
case .response(403): "Diese Aktion ist nicht erlaubt. Bitte prüfe deine Berechtigungen."
case .response(409): "Der Eintrag wurde inzwischen geändert. Bitte neu laden."
case .response(429): "Zu viele Versuche. Bitte warte einen Moment."
case .response(let status): "Die Anfrage konnte nicht abgeschlossen werden (HTTP \(status))."
case .invalidPath, .invalidResponse: "Der Server hat keine gültige API-Antwort geliefert."
case .unavailable: "Verbindung fehlgeschlagen. Bitte Server, Zertifikat und Netzwerk prüfen."
case .closed: "Diese Sitzung wurde geschlossen."
}
}
}