# iOS-App für YpChat – Umsetzungsplan Dieses Dokument beschreibt die **komplette** Planung einer nativen iOS-App mit **Feature-Parität** zur bestehenden Android-App (`android/app`, Paket `de.ypchat.android`). Die Android-Implementierung dient als fachliche und API-Referenz. --- ## 1. Ziele und Abgrenzung ### 1.1 Produktziel - Nutzer können sich wie in der Android-App anmelden, chatten, suchen, Posteingang/Verlauf nutzen, Konsole-Befehle senden, Feedback und Partner-Links einsehen sowie Bilder hochladen und versenden. - Gleiche Backend-URLs, REST-Endpunkte und Socket.IO-Ereignisse wie auf Android. ### 1.2 Nicht-Ziele (optional später) - WatchOS, iPad-spezifisches Layout (erst iPhone-first). - Eigener Push-Benachrichtigungsdienst (nur falls das Backend später APNs unterstützt). --- ## 2. Referenz: Android-Architektur (zu spiegeln) | Schicht | Android | iOS-Empfehlung | |--------|---------|----------------| | Konfiguration | `BuildConfig.BASE_URL` / `local.properties` | Xcode Build-Konfiguration + `xcconfig` oder Info-Key `BASE_URL` | | DI / Container | `AppContainer` | Eigene `AppServices`-Klasse oder schlankes Factory-Pattern beim App-Start | | HTTP + Cookies | OkHttp + `SessionCookieJar` | `URLSession` mit `HTTPCookieStorage` (shared oder app-group bei Bedarf) | | REST | Retrofit + Gson | `URLSession` + `Codable` (oder optional Alamofire) | | Echtzeit | `io.socket` (Socket.IO) | [socket.io-client-swift](https://github.com/socketio/socket.io-client-swift) (oder vergleichbare aktive Library) | | Zustand | `ChatRepository` + `StateFlow` | `ObservableObject` / `@Observable` + `async` oder Combine | | UI | Jetpack Compose | **SwiftUI** (empfohlen) | | Profil lokal | `ProfileStore` (SharedPreferences) | `UserDefaults` oder kleines Keychain-Wrapper nur wenn sensibel | Wichtige Dateien zum Abgleich: `AppContainer.kt`, `RestApi.kt`, `SocketClient.kt`, `ChatRepository.kt`, `ChatViewModel.kt`, `YpChatRoot.kt`, `Models.kt`, `SocketEvent.kt`. --- ## 3. Technologie- und Projektentscheidungen ### 3.1 Sprache und UI - **Swift 5.10+**, Deployment **iOS 17+** (oder 16, wenn Gerätebindung es erfordert – dann API prüfen). - **SwiftUI** für alle Screens; Navigation: `TabView` + eingebettete Unternavigation für „Mehr“. ### 3.2 Abhängigkeiten - **Socket.IO-Client** (Swift-Paket via SPM): muss dieselben Transporte unterstützen wie Android (`websocket` + `polling`); Verbindungsoptionen an `SocketClient.kt` anlehnen (Reconnect, Timeout). - **Kein** Retrofit – native `URLSession` reicht für die überschaubare REST-Oberfläche. - Bilder: **PhotosUI** (`PhotosPicker` / `PHPicker`) für die Bildauswahl; Upload als `multipart/form-data` wie Android. ### 3.3 Bundle-ID und Naming - Vorschlag: `de.ypchat.ios` oder konsistent mit Android `de.ypchat.android` → z. B. `de.ypchat.app` (einheitlich mit Marketing/Store). - Anzeigename: wie `R.string.app_name` auf Android. --- ## 4. Konfiguration und Build-Varianten ### 4.1 Base-URL - Standard wie Android: `https://www.ypchat.net` (siehe `defaultBaseUrl` in `android/app/build.gradle.kts`). - Pro Build-Konfiguration überschreibbar: - **Debug**: lokale `Config/Debug.xcconfig` mit `BASE_URL = https://…` (oder Staging). - **Release**: feste Produktions-URL. - Zur Laufzeit: `Bundle` / generierte `Info`-Keys auslesen, trailing slash wie `AppConfig.kt` entfernen. ### 4.2 App Transport Security (ATS) - Produktion: HTTPS wie Android Release (`usesCleartextTraffic` false). - Debug: nur bei Bedarf `NSAppTransportSecurity` für HTTP-Testserver – dokumentieren und nicht in Release aktiv lassen. ### 4.3 Berechtigungen (Info.plist) - **Foto-Bibliothek** (Lesen): `NSPhotoLibraryUsageDescription` mit klarer Begründung (Bild im Chat senden). - Keine Kamera zwingend nötig, wenn nur Galerie wie Android `PickVisualMedia`. --- ## 5. Datenmodell (Codable) Alle DTOs aus `Models.kt` 1:1 als `struct` mit `Codable` abbilden, inkl. Sonderfall **`CountriesResponse`**: auf Android ein `LinkedHashMap` – auf iOS als `[String: String]` decodieren oder eigener `Decodable`-Wrapper. **PartnerLinkDto**: JSON-Feld `"Page Name"` → `CodingKeys` mit `case pageName = "Page Name"`. --- ## 6. REST-API Basis-URL + Pfad wie `RestApi.kt`: | Methode | Pfad | Zweck | |---------|------|--------| | GET | `api/session` | Session / eingeloggter User | | POST | `api/logout` | Logout | | GET | `api/countries` | Länderliste | | GET | `api/feedback` | Feedback-Liste | | GET | `api/feedback/admin-status` | Admin-Session-Status | | POST | `api/feedback` | Feedback senden | | POST | `api/feedback/admin-login` | Admin-Login | | POST | `api/feedback/admin-logout` | Admin-Logout | | DELETE | `api/feedback/{id}` | Eintrag löschen (Admin) | | GET | `api/partners` | Partner-Links | | POST | `api/upload-image` | Multipart-Feld `image` | **Cookie-Handling:** Nach `session`-Call und weiteren Requests müssen Cookies wie im Browser/OkHttp mitgeführt werden – `URLSessionConfiguration.default` mit `httpCookieStorage` und `httpShouldSetCookies` / `httpCookieAcceptPolicy` prüfen; bei Problemen explizit `Cookie`-Header aus Storage für die Domain setzen. **Fehlerbehandlung:** HTTP-Status und Body auswerten; Fehlermeldungen in den UI-State wie `ChatState.errorMessage` / `feedbackMessage`. --- ## 7. Socket.IO-Client ### 7.1 Verbindung - URL: gleiche `baseUrl` wie REST (ohne zusätzlichen Pfad, sofern Server Root nutzt – mit Android-Verhalten abgleichen). - Optionen analog `SocketClient.kt`: Reconnect, `reconnectionAttempts`, Delays, Timeout; Transports WebSocket + Polling falls die Swift-Library das abbildet. ### 7.2 Authentifizierung / Session - Beim Connect bzw. nach Connect: `setSessionId` mit Payload `{ "expressSessionId": "" }` – identisch zu Android. - `pendingExpressSessionId` bei erneutem Connect erneut senden. ### 7.3 Client → Server (emit) | Event | Payload-Felder (Kern) | |-------|------------------------| | `login` | userName, gender, age, country, expressSessionId | | `message` | message, messageId, optional toUserName; für Bild: toUserName, isImage, imageUrl | | `requestConversation` | withUserName | | `userSearch` | nameIncludes, minAge, maxAge, countries[], genders[] | | `requestHistory` | (leer) | | `requestOpenConversations` | (leer) | | `blockUser` / `unblockUser` | userName | ### 7.4 Server → Client (on) Alle Events aus `SocketClient.kt` abbilden und in ein internes `enum` / sealed Äquivalent übersetzen: `connected`, `loginSuccess`, `userList`, `message`, `messageSent`, `conversation`, `searchResults`, `historyResults`, `inboxResults`, `unreadChats`, `userBlocked`, `userUnblocked`, `commandResult`, `commandTable`, `error`, sowie Verbindungsmetadaten (`connect`, `disconnect`, `connect_error`). **JSON-Parsing:** Foundation `JSONSerialization` oder `JSONDecoder` mit `[String: Any]`-Hilfstypen – Feldnamen und Typen strikt an Android-Mapper (`toUserDto`, `toMessageDto`, …) halten. ### 7.5 HTTP-Stack und Socket - Android nutzt **dieselbe** `OkHttpClient`-Instanz für Socket und REST. Auf iOS: wo möglich eine gemeinsame Cookie-Quelle; falls die Socket-Library keinen `URLSession` teilt, nach dem Connect sicherstellen, dass `expressSessionId` per `setSessionId` gesetzt ist (wie Android bei getrenntem Transport). --- ## 8. Repository- und App-Logik (`ChatRepository`) Die Reducer-Logik aus `ChatRepository.reduce` und die Methoden **funktional kopieren**: - **restoreSession:** Profil laden, Länder laden, `feedbackAdminStatus`, `GET api/session`, dann Socket connect + `setSessionId`. - **login:** Profil speichern, Session-ID beschaffen, Socket ggf. verbinden, `login` emit. - **logout:** `POST api/logout`, Socket trennen, Cookies löschen, State reset mit erhaltenem `savedProfile` und `countries`. - **Timeout:** 30 Minuten (`1800` s) – Ticker jede Sekunde, bei 0 automatisch `logout` (wie `startTimeoutTicker` / `resetTimeout`). - **openConversation / closeConversation**, **sendMessage** (inkl. Konsolen-Befehle mit `/`), **search**, **inbox/history**, **block/unblock**. - **Bild:** Upload REST → bei Erfolg `sendImage` mit absoluter URL (Basis-URL voranstellen wenn relativ). Zustand als eine **`ChatState`-Struktur** (alle Properties aus `ChatRepository.kt`). --- ## 9. Präsentationsschicht (SwiftUI) ### 9.1 ViewModel - `@MainActor` ViewModel (oder Repository auf MainActor publizieren), das `ChatState` published und User-Aktionen an das Repository delegiert. - `task { await repository.restoreSession() }` beim Erscheinen der Root-View. ### 9.2 Bildschirme (Parität zu `YpChatRoot`) 1. **Login:** Landing-Karte, Profilfelder, Gender/Country-Picker, Validierung (Nickname ≥ 3 Zeichen), Socket-Status. 2. **Haupt-Shell:** Top-Bar (App-Name, User, Online-Status, Timeout-Countdown, Logout). 3. **Tabs:** Online | Suche | Posteingang | Verlauf | Konsole | Mehr – wie Android; Posteingang-Badge mit `unreadChatsCount`. 4. **Chat:** Zurück, Blockieren/Entblocken, Nachrichtenliste, Eingabe, Smiley-Leiste (gleiche Tokens wie `SmileyItems`), Bild wählen, Senden, Upload-Banner. 5. **Suche / Inbox / Verlauf:** Listen wie Android. 6. **Konsole:** Eingabe, Senden, Ausgabezeilen + Tabelle (`CommandTableState`). 7. **Mehr:** Unterseiten Feedback (inkl. Admin-Login), Partner (Safari öffnen), FAQ, Regeln, Sicherheit, Impressum – Texte aus **lokalisierten Strings** (siehe `res/values/strings.xml` auf Android als Quelle für DE/EN). ### 9.3 Design - Farben und Abstände an die Android-Farbkonstanten in `YpChatRoot.kt` anlehnen für ein konsistentes Erscheinungsbild. - Bilder in Chat: AsyncImage / Kingfisher nur falls nötig; Caching beachten. ### 9.4 Lokalisierung - `Localizable.xcstrings` (oder `.strings`) – mindestens Deutsch; Android-`strings.xml` als Master-Liste. --- ## 10. Sicherheit und Datenschutz - Passwörter (Feedback-Admin) nur im Speicher, nicht loggen. - TLS in Release erzwingen. - Keychain nur nötig, wenn sensible Tokens dauerhaft ohne Cookies gespeichert werden sollen (aktuell: Session eher cookie-basiert wie Web). --- ## 11. Qualitätssicherung ### 11.1 Tests - **Unit-Tests:** JSON-Decodierung der REST- und Socket-Payloads (Fixtures aus echten Server-Responses erfassen). - **Integration:** Manuell gegen Staging/Produktion: Login, Nachricht, Suche, Feedback, Bild-Upload, Logout, Timeout. ### 11.2 Edge Cases - App in den Hintergrund: Socket-Verhalten (Disconnect/Reconnect) und Timeout-Reset gemäß Produktregel klären (Android tickt weiter – iOS analog umsetzen). - Schlechte Netzwerke: Fehlermeldungen aus `SocketEvent.Error` und REST anzeigen. --- ## 12. App Store / Vertrieb - **Apple Developer Program**, App-ID, Provisioning Profiles. - **Datenschutzerklärung** und **App-Privacy-Labels** (Netzwerk, Fotos, ggf. Nutzerinhalt). - **TestFlight** für interne Tester vor Release. - Versionierung: an `versionName` / `versionCode` der Android-App angleichen oder eigenes Schema dokumentieren. --- ## 13. Repository-Struktur (Vorschlag) ``` ios/ YpChat.xcodeproj oder YpChat.xcworkspace YpChat/ App/ Core/ # AppConfig, Cookie/Session storage, ProfileStore Data/ # APIClient, SocketClient, DTOs Features/ # Login, Chat, Tabs, More, … Resources/ # Assets, Localizable Config/ Debug.xcconfig Release.xcconfig ``` Root-`README` oder dieses Dokument verlinken, wie man `BASE_URL` setzt. --- ## 14. Phasenplan (Meilensteine) | Phase | Inhalt | Ergebnis | |-----|--------|----------| | **P0** | Xcode-Projekt, Konfiguration, leere SwiftUI-App, BASE_URL | Laufende Shell-App | | **P1** | REST-Client + Cookie-Speicher, `session` / `countries` / `logout` | Session funktioniert | | **P2** | Socket-Client, `setSessionId`, `login`, `userList`, `message` | Minimaler Chat | | **P3** | Vollständiger `ChatRepository`-State, alle Socket-Events, Timeout | Parität Kernlogik | | **P4** | SwiftUI: Login + Tabs + Chat + Suche/Inbox/Verlauf | UI-Hauptteil | | **P5** | Bild-Upload + Anzeige, Smileys | Medien-Parität | | **P6** | Mehr: Feedback, Partner, statische Seiten | Rest-UI | | **P7** | Lokalisierung, Feinschliff, Dark Mode optional | Polish | | **P8** | TestFlight, Store-Metadaten, Screenshots | Release | --- ## 15. Risiken und Abhängigkeiten - **Socket.IO-Swift-Version** muss zum **Server-Socket.IO** passen; bei Protokoll-Mismatch Verbindungsfehler – mit Server-Version abgleichen. - **Cookie-Domain / Pfad:** muss mit dem Backend identisch sein, sonst wirkt `session` wie „nicht eingeloggt“. - **Bildgröße:** 5 MB-Limit clientseitig wie Android (`MAX_IMAGE_BYTES`). --- ## 16. Nächster konkreter Schritt 1. Im Repo Ordner `ios/` anlegen und Xcode-Projekt (SwiftUI App) hinzufügen. 2. SPM: Socket.IO-Client einbinden, minimales `SocketClient`-Swift gegen Staging testen. 3. `GET api/session` mit gemeinsamem Cookie-Storage verifizieren. 4. Danach schrittweise `ChatRepository` portieren und UI anbinden. --- *Stand: Abgleich mit Android-Codebase (Compose-App, Mai 2026). Bei Backend-Änderungen diesen Plan und die Event-Liste aktualisieren.*