slogan3

Help me bij het implementeren van een aangepaste Swift-netwerklaag voor mijn iOS-app die geavanceerde foutafhandeling en caching-strategieën ondersteunt.

description

Door het implementeren van een aangepaste netwerklaag verbeter je de gebruikerservaring door snellere laadtijden, verbeterd foutbeheer en verminderd netwerkgebruik, wat leidt tot een efficiëntere app overall.

prompt

try_prompt

Help me een aangepaste netwerklaag in Swift te ontwerpen voor mijn iOS-toepassing. Deze laag moet API-verzoeken efficiënt afhandelen, fouten beheren met duidelijke gebruikersfeedback en caching-strategieën implementeren om netwerkverzoeken te ... more
generate_helper
disclaimerOnPageApi image_legal_disclaimer...

ai_answers

provider: openai
model: gpt-4.1-nano
Natuurlijk! Hieronder vind je een gestructureerde aanpak en codevoorbeeld voor een aangepaste netwerklaag in Swift, gericht op efficiëntie, foutafhandeling en caching, geschikt voor het ophalen van gebruikersprofiel en instellingen. --- ### 1. Structuur van de netwerklaag - **APIClient**: Een centrale klasse voor het uitvoeren van netwerkverzoeken. - **Endpoint**: Enum of struct voor API-eindpunten. - **NetworkError**: Enum voor foutafhandeling. - **CacheManager**: Voor eenvoudige cachingstrategieën. --- ### 2. Codevoorbeeld ```swift import Foundation // 1. Definieer API-eindpunten enum APIEndpoint { case userProfile case userSettings var url: URL { switch self { case .userProfile: return URL(string: "https://api.example.com/user/profile")! case .userSettings: return URL(string: "https://api.example.com/user/settings")! } } } // 2. Foutenbeheer enum NetworkError: Error { case invalidResponse case invalidData case serverError(String) case cacheMiss case unknown } // 3. CacheManager voor eenvoudige caching class CacheManager { static let shared = CacheManager() private var cache = NSCache<NSURL, NSData>() func getCachedResponse(for url: URL) -> Data? { return cache.object(forKey: url as NSURL) as Data? } func cacheResponse(_ data: Data, for url: URL) { cache.setObject(data as NSData, forKey: url as NSURL) } } // 4. APIClient class APIClient { static let shared = APIClient() private let session: URLSession private init() { let configuration = URLSessionConfiguration.default configuration.requestCachePolicy = .returnCacheDataElseLoad self.session = URLSession(configuration: configuration) } // Algemene request functie func request<T: Decodable>(_ endpoint: APIEndpoint, responseType: T.Type, completion: @escaping (Result<T, NetworkError>) -> Void) { let url = endpoint.url // Check cache eerst if let cachedData = CacheManager.shared.getCachedResponse(for: url) { do { let decoded = try JSONDecoder().decode(T.self, from: cachedData as Data) completion(.success(decoded)) return } catch { // Cache kan corrupt zijn; verder gaan met netwerk } } // Maak URLRequest var request = URLRequest(url: url) request.httpMethod = "GET" // Voer request uit session.dataTask(with: request) { data, response, error in // Foutcontrole if let error = error { DispatchQueue.main.async { completion(.failure(.serverError(error.localizedDescription))) } return } guard let httpResponse = response as? HTTPURLResponse else { DispatchQueue.main.async { completion(.failure(.invalidResponse)) } return } guard (200...299).contains(httpResponse.statusCode) else { let message = HTTPURLResponse.localizedString(forStatusCode: httpResponse.statusCode) DispatchQueue.main.async { completion(.failure(.serverError(message))) } return } guard let data = data else { DispatchQueue.main.async { completion(.failure(.invalidData)) } return } // Decodeer data do { let decodedObject = try JSONDecoder().decode(T.self, from: data) // Cacheer de response CacheManager.shared.cacheResponse(data, for: url) DispatchQueue.main.async { completion(.success(decodedObject)) } } catch { DispatchQueue.main.async { completion(.failure(.invalidData)) } } }.resume() } } ``` --- ### 3. Gebruik in je app ```swift // Voorbeeld: ophalen van gebruikersprofiel APIClient.shared.request(UserProfile.self, responseType: UserProfile.self) { result in switch result { case .success(let profile): // Update UI of data verwerken print("Gebruikersnaam: \(profile.name)") case .failure(let error): // Foutmelding tonen print("Fout bij ophalen profiel: \(error.localizedDescription)") } } ``` --- ### 4. Best practices - **Gebruik decodable structs** voor API-responsen. - **Caché strategie**: Cache responses na eerste fetch om netwerkverzoeken te minimaliseren. - **Foutafhandeling**: Geef duidelijke foutmeldingen en feedback aan de gebruiker. - **URLSession configuratie**: Gebruik `URLSessionConfiguration` voor caching en timeouts. - **Main thread**: Zorg dat UI-updates op de hoofdthread gebeuren. --- ### Samenvatting Deze aanpak biedt een solide basis voor een efficiënte, foutbestendige en cache-ondersteunde netwerklaag in Swift. Pas de structuren en foutmeldingen aan op basis van je exacte API en app-vereisten.
provider: deepseek
model: deepseek-chat
Ik help je graag met het ontwerpen van een aangepaste netwerklaag in Swift. Hier is een complete implementatie met best practices: ## 1. Basis Netwerklaag Structuur ```swift import Foundation // MARK: - Netwerk Fouten enum NetworkError: Error, LocalizedError { case invalidURL case invalidResponse case serverError(Int) case decodingError case noInternetConnection case timeout case unauthorized var errorDescription: String? { switch self { case .invalidURL: return "Ongeldige URL" case .invalidResponse: return "Ongeldig server antwoord" case .serverError(let code): return "Server fout: \(code)" case .decodingError: return "Fout bij verwerken gegevens" case .noInternetConnection: return "Geen internetverbinding" case .timeout: return "Time-out bij verbinding" case .unauthorized: return "Niet geautoriseerd" } } } // MARK: - HTTP Methoden enum HTTPMethod: String { case get = "GET" case post = "POST" case put = "PUT" case delete = "DELETE" } // MARK: - API Endpoints enum APIEndpoint { case userProfile case userSettings var path: String { switch self { case .userProfile: return "/api/user/profile" case .userSettings: return "/api/user/settings" } } var method: HTTPMethod { switch self { case .userProfile, .userSettings: return .get } } } ``` ## 2. Request Builder ```swift // MARK: - Network Request struct NetworkRequest { let endpoint: APIEndpoint let parameters: [String: Any]? let headers: [String: String]? init(endpoint: APIEndpoint, parameters: [String: Any]? = nil, headers: [String: String]? = nil) { self.endpoint = endpoint self.parameters = parameters self.headers = headers } func buildURLRequest(baseURL: String) throws -> URLRequest { guard let url = URL(string: baseURL + endpoint.path) else { throw NetworkError.invalidURL } var urlRequest = URLRequest(url: url) urlRequest.httpMethod = endpoint.method.rawValue urlRequest.timeoutInterval = 30 // Headers toevoegen headers?.forEach { key, value in urlRequest.setValue(value, forHTTPHeaderField: key) } // Parameters verwerken if let parameters = parameters, !parameters.isEmpty { if endpoint.method == .get { var components = URLComponents(url: url, resolvingAgainstBaseURL: false) components?.queryItems = parameters.map { URLQueryItem(name: $0.key, value: "\($0.value)") } urlRequest.url = components?.url } else { urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") urlRequest.httpBody = try JSONSerialization.data(withJSONObject: parameters) } } return urlRequest } } ``` ## 3. Cache Manager ```swift // MARK: - Cache Manager class CacheManager { static let shared = CacheManager() private let cache = NSCache<NSString, AnyObject>() private let userDefaults = UserDefaults.standard private init() {} func cache<T: Codable>(_ object: T, for key: String) { if let encoded = try? JSONEncoder().encode(object) { userDefaults.set(encoded, forKey: key) userDefaults.synchronize() } } func getCached<T: Codable>(for key: String) -> T? { guard let data = userDefaults.data(forKey: key) else { return nil } return try? JSONDecoder().decode(T.self, from: data) } func removeCached(for key: String) { userDefaults.removeObject(forKey: key) } func clearAllCache() { let domain = Bundle.main.bundleIdentifier! userDefaults.removePersistentDomain(forName: domain) userDefaults.synchronize() } } ``` ## 4. Hoofd Netwerk Manager ```swift // MARK: - Network Manager class NetworkManager { static let shared = NetworkManager() private let session: URLSession private let cacheManager = CacheManager.shared private let baseURL = "https://jouw-api-domein.com" // Vervang met jouw API URL private init() { let configuration = URLSessionConfiguration.default configuration.timeoutIntervalForRequest = 30 configuration.timeoutIntervalForResource = 60 configuration.requestCachePolicy = .returnCacheDataElseLoad self.session = URLSession(configuration: configuration) } // MARK: - Generieke Request Functie func request<T: Codable>( _ request: NetworkRequest, cacheKey: String? = nil, forceRefresh: Bool = false ) async throws -> T { // Cache controleren (indien van toepassing) if let cacheKey = cacheKey, !forceRefresh { if let cached: T = cacheManager.getCached(for: cacheKey) { print("📦 Gebruik gecachte data voor: \(cacheKey)") return cached } } // Netwerk request bouwen let urlRequest = try request.buildURLRequest(baseURL: baseURL) // Network connectivity controleren guard await isConnectedToNetwork() else { throw NetworkError.noInternetConnection } print("🌐 Verstuur request: \(urlRequest.url?.absoluteString ?? "")") do { let (data, response) = try await session.data(for: urlRequest) guard let httpResponse = response as? HTTPURLResponse else { throw NetworkError.invalidResponse } // HTTP status code verwerken try handleStatusCode(httpResponse.statusCode) // Data decoderen let decodedData = try JSONDecoder().decode(T.self, from: data) // Cache data (indien van toepassing) if let cacheKey = cacheKey { cacheManager.cache(decodedData, for: cacheKey) } return decodedData } catch let error as NetworkError { throw error } catch let error as DecodingError { print("❌ Decoding error: \(error)") throw NetworkError.decodingError } catch { if let urlError = error as? URLError { switch urlError.code { case .timedOut: throw NetworkError.timeout case .notConnectedToInternet: throw NetworkError.noInternetConnection default: throw NetworkError.invalidResponse } } throw NetworkError.invalidResponse } } // MARK: - Helper Methods private func handleStatusCode(_ statusCode: Int) throws { switch statusCode { case 200...299: return // Success case 401: throw NetworkError.unauthorized case 400...499: throw NetworkError.serverError(statusCode) case 500...599: throw NetworkError.serverError(statusCode) default: throw NetworkError.invalidResponse } } private func isConnectedToNetwork() async -> Bool { // Vereist Network framework import // In praktijk: gebruik Network framework of Reachability return true // Vereenvoudigd voor dit voorbeeld } } ``` ## 5. Data Modellen ```swift // MARK: - Data Modellen struct UserProfile: Codable { let id: String let name: String let email: String let avatarURL: String? enum CodingKeys: String, CodingKey { case id, name, email case avatarURL = "avatar_url" } } struct UserSettings: Codable { let notificationsEnabled: Bool let theme: String let language: String enum CodingKeys: String, CodingKey { case notificationsEnabled = "notifications_enabled" case theme, language } } ``` ## 6. Gebruiksvoorbeelden ```swift // MARK: - Gebruik in ViewModel/Controller class UserService { private let networkManager = NetworkManager.shared // Gebruikersprofiel ophalen func fetchUserProfile() async throws -> UserProfile { let request = NetworkRequest(endpoint: .userProfile) do { let profile: UserProfile = try await networkManager.request( request, cacheKey: "user_profile", forceRefresh: false ) return profile } catch { throw error } } // Gebruikersinstellingen ophalen func fetchUserSettings() async throws -> UserSettings { let request = NetworkRequest(endpoint: .userSettings) do { let settings: UserSettings = try await networkManager.request( request, cacheKey: "user_settings", forceRefresh: false ) return settings } catch { throw error } } // Cache wissen func clearCache() { CacheManager.shared.clearAllCache() } } // MARK: - Gebruik in View Controller class ProfileViewController: UIViewController { private let userService = UserService() func loadUserData() { Task { do { // Laad beide requests parallel async let profile = userService.fetchUserProfile() async let settings = userService.fetchUserSettings() let (userProfile, userSettings) = try await (profile, settings) await MainActor.run { self.updateUI(with: userProfile, settings: userSettings) } } catch let error as NetworkError { await MainActor.run { self.showError(error.errorDescription ?? "Onbekende fout") } } catch { await MainActor.run { self.showError("Er is een fout opgetreden") } } } } private func updateUI(with profile: UserProfile, settings: UserSettings) { // Update UI } private func showError(_ message: String) { let alert = UIAlertController( title: "Fout", message: message, preferredStyle: .alert ) alert.addAction(UIAlertAction(title: "OK", style: .default)) present(alert, animated: true) } } ``` ## Best Practices: 1. **Error Handling**: Gebruik gedetailleerde foutmeldingen met lokalisatie 2. **Caching**: Implementeer strategie gebaseerd op data type en frequentie 3. **Modulariteit**: Houd netwerklaag gescheiden van business logic 4. **Testbaarheid**: Maak protocol-based voor eenvoudig testen 5. **Performance**: Gebruik async/await voor niet-blokkerende calls 6. **Security**: Voeg authenticatie headers toe waar nodig Deze implementatie biedt een solide basis die je kunt uitbreiden met extra features zoals: - Retry mechanisme - Request interceptors - Upload/download progress tracking - Request batching - Offline ondersteuning Laat me weten als je specifieke onderdelen wilt uitbreiden!