ReadKeep/readeck/Data/API/API.swift
Ilyas Hallak 98a914cb2e feat: Implementierung der Readeck API mit kompletter Architektur
- API-Klasse mit allen CRUD-Operationen für Bookmarks
  - Login/Authentifizierung mit Bearer Token
  - Bookmarks abrufen (Liste und Details)
  - Artikel-Inhalt abrufen
- DTO-Strukturen in separate Dateien aufgeteilt
  - UserDto für Authentifizierung
  - BookmarkDto für Bookmark-Listen
  - BookmarkDetailDto mit vollständigen Metadaten
- MVVM-Architektur mit @Observable
  - SettingsViewModel für Anmeldung
  - BookmarksViewModel für Bookmark-Verwaltung
- SwiftUI Views mit modernem Design
  - SettingsView mit Eingabefeldern und Validierung
  - BookmarksView mit Pull-to-Refresh und Leerzustand
  - MainTabView als Navigation
- Use Case Pattern implementiert
  - LoginUseCase für Authentifizierung
  - GetBookmarksUseCase für Datenabfrage
  - DefaultUseCaseFactory für Dependency Injection
- Fehlerbehandlung und Loading States
- Protocol-basierte Architektur für bessere Testbarkeit
2025-06-11 11:02:19 +02:00

140 lines
4.5 KiB
Swift

//
// API.swift
// readeck
//
// Created by Ilyas Hallak on 10.06.25.
//
import Foundation
enum APIError: Error {
case invalidURL
case networkError
case invalidResponse
case authenticationFailed
}
protocol PAPI {
func login(username: String, password: String) async throws -> UserDto
func getBookmarks() async throws -> [BookmarkDto]
func getBookmark(id: String) async throws -> BookmarkDetailDto
func getBookmarkArticle(id: String) async throws -> String
var authToken: String? { get set }
}
class API: PAPI {
private let baseURL: String
var authToken: String?
init(baseURL: String) {
self.baseURL = baseURL
}
func login(username: String, password: String) async throws -> UserDto {
guard let url = URL(string: "\(baseURL)/auth") else {
throw APIError.invalidURL
}
let credentials = [
"username": username,
"password": password,
"application": "api doc"
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "accept")
request.httpBody = try? JSONSerialization.data(withJSONObject: credentials)
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 201 else {
throw APIError.authenticationFailed
}
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let token = json["token"] as? String {
self.authToken = token
let decoder = JSONDecoder()
return try decoder.decode(UserDto.self, from: data)
}
throw APIError.invalidResponse
}
// Bookmarks abrufen
func getBookmarks() async throws -> [BookmarkDto] {
guard let url = URL(string: "\(baseURL)/bookmarks") else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "accept")
if let token = authToken {
request.setValue("Bearer \(token)", forHTTPHeaderField: "authorization")
}
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw APIError.networkError
}
let decoder = JSONDecoder()
return try decoder.decode([BookmarkDto].self, from: data)
}
func getBookmark(id: String) async throws -> BookmarkDetailDto {
guard let url = URL(string: "\(baseURL)/bookmarks/\(id)") else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "accept")
if let token = authToken {
request.setValue("Bearer \(token)", forHTTPHeaderField: "authorization")
}
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw APIError.networkError
}
let decoder = JSONDecoder()
return try decoder.decode(BookmarkDetailDto.self, from: data)
}
func getBookmarkArticle(id: String) async throws -> String {
guard let url = URL(string: "\(baseURL)/bookmarks/\(id)/article") else {
throw APIError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("text/html", forHTTPHeaderField: "accept")
if let token = authToken {
request.setValue("Bearer \(token)", forHTTPHeaderField: "authorization")
}
let (data, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200,
let htmlContent = String(data: data, encoding: .utf8) else {
throw APIError.networkError
}
return htmlContent
}
}