# 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<String, String>` – 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": "<id>" }` – 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.*
