276 lines
13 KiB
Markdown
Executable File
276 lines
13 KiB
Markdown
Executable File
# 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.*
|