// // NetworkService.swift // EmployeeDirectory // // Created by Matt Bruce on 1/20/25. // import Foundation public enum NetworkServiceError: Error { /// The response from the server was invalid (e.g., non-200 status code or malformed URL). case invalidResponse /// The url giving is invalid or malformed case invalidURL /// The data received was invalid or could not be decoded. case decodingError(DecodingError) /// A network-related error occurred. case networkError(URLError) /// An unexpected, uncategorized error occurred. case unknownError(Error) } public class NetworkService { // MARK: - Properties public static let shared = NetworkService() // Default shared instance private let session: URLSession // MARK: - Initializer /// Public initializer to allow customization public init(session: URLSession = .shared) { self.session = session } // MARK: - Public Methods /// Fetches data from a URL and decodes it into a generic Decodable type. /// - Parameters: /// - endpoint: The url to fetch data from. /// - type: The type to decode the data into. /// - Throws: A `ServiceError` for network, decoding, or unexpected errors. /// - Returns: The decoded object of the specified type. public func fetchData(from endpoint: String, as type: T.Type) async throws -> T { do { //ensure a valid URL guard let url = URL(string: endpoint) else { throw NetworkServiceError.invalidURL } // Perform network request let (data, response) = try await URLSession.shared.data(for: URLRequest(url: url)) // Validate HTTP response guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { throw NetworkServiceError.invalidResponse } // Decode the response into the specified type return try JSONDecoder().decode(T.self, from: data) } catch let urlError as URLError { throw NetworkServiceError.networkError(urlError) } catch let decodingError as DecodingError { throw NetworkServiceError.decodingError(decodingError) } catch { throw NetworkServiceError.unknownError(error) } } }