diff --git a/EmployeeDirectory/Protocols/EmployeeServiceProtocol.swift b/EmployeeDirectory/Protocols/EmployeeServiceProtocol.swift index b9a161e..371cbb6 100644 --- a/EmployeeDirectory/Protocols/EmployeeServiceProtocol.swift +++ b/EmployeeDirectory/Protocols/EmployeeServiceProtocol.swift @@ -8,8 +8,7 @@ /// This will be the interface for the API for Employees public protocol EmployeeServiceProtocol { /// This will get a list of all employees - /// - Parameter serviceMode: Mode in which to hit. /// - Returns: An Employees struct - func getEmployees(_ serviceMode: EmployeeServiceMode) async throws -> Employees - func getEmployees(_ serviceMode: EmployeeServiceMode, page: Int, perPage: Int, sortField: EmployeeSortField, sortOrder: EmployeeSortOrder) async throws -> Employees + func getEmployees() async throws -> Employees + func getEmployees(page: Int, perPage: Int, sortField: EmployeeSortField, sortOrder: EmployeeSortOrder) async throws -> Employees } diff --git a/EmployeeDirectory/Services/EmployeeService.swift b/EmployeeDirectory/Services/EmployeeService.swift index 8871645..86ab5d2 100644 --- a/EmployeeDirectory/Services/EmployeeService.swift +++ b/EmployeeDirectory/Services/EmployeeService.swift @@ -18,11 +18,23 @@ public enum EmployeeSortOrder: String { } /// These are the testing URL Endpoints for different states -public enum EmployeeServiceMode: String, CaseIterable { +internal enum EmployeeServiceMode: String, CaseIterable { case production case malformed case empty + public var service: EmployeeServiceProtocol { + switch self { + case .production: + return MockEmployeeService.shared + //return EmployeeService.shared + case .malformed: + return EmployeeMalformedService.shared + case .empty: + return EmployeeEmptyService.shared + } + } + /// Enpoint in which to grabe employees from. public var endpoint: String { switch self { @@ -50,8 +62,8 @@ public class EmployeeService: EmployeeServiceProtocol { /// This will get a list of all employees /// - Parameter serviceMode: Mode in which to hit. /// - Returns: An Employees struct - public func getEmployees(_ serviceMode: EmployeeServiceMode = .production) async throws -> Employees { - return try await NetworkService.shared.fetchData(from: serviceMode.endpoint, as: Employees.self) + public func getEmployees() async throws -> Employees { + return try await NetworkService.shared.fetchData(from: EmployeeServiceMode.production.endpoint, as: Employees.self) } /// Fetch employees with pagination support @@ -59,12 +71,11 @@ public class EmployeeService: EmployeeServiceProtocol { /// - page: The page number to fetch. /// - perPage: The number of employees per page. /// - Returns: A paginated Employees object. - public func getEmployees(_ serviceMode: EmployeeServiceMode = .production, - page: Int, perPage: Int, + public func getEmployees(page: Int, perPage: Int, sortField: EmployeeSortField = .fullName, sortOrder: EmployeeSortOrder = .ascending) async throws -> Employees { - guard var urlComponents = URLComponents(string: serviceMode.endpoint) else { + guard var urlComponents = URLComponents(string: EmployeeServiceMode.production.endpoint) else { throw NetworkServiceError.invalidURL } @@ -84,3 +95,49 @@ public class EmployeeService: EmployeeServiceProtocol { return try await NetworkService.shared.fetchData(with: request, as: Employees.self) } } + +/// Service Layer for Employees +public class EmployeeMalformedService: EmployeeServiceProtocol { + // MARK: - Properties + public static let shared = EmployeeMalformedService() // Default shared instance + + // MARK: - Initializer + + public init() {} + + // MARK: - Public Methods + + /// This will get a list of all employees + /// - Parameter serviceMode: Mode in which to hit. + /// - Returns: An Employees struct + public func getEmployees() async throws -> Employees { + return try await NetworkService.shared.fetchData(from: EmployeeServiceMode.malformed.endpoint, as: Employees.self) + } + + public func getEmployees(page: Int, perPage: Int, sortField: EmployeeSortField, sortOrder: EmployeeSortOrder) async throws -> Employees { + return try await NetworkService.shared.fetchData(from: EmployeeServiceMode.malformed.endpoint, as: Employees.self) + } +} + +/// Service Layer for Employees +public class EmployeeEmptyService: EmployeeServiceProtocol { + // MARK: - Properties + public static let shared = EmployeeEmptyService() // Default shared instance + + // MARK: - Initializer + + public init() {} + + // MARK: - Public Methods + + /// This will get a list of all employees + /// - Parameter serviceMode: Mode in which to hit. + /// - Returns: An Employees struct + public func getEmployees() async throws -> Employees { + return try await NetworkService.shared.fetchData(from: EmployeeServiceMode.empty.endpoint, as: Employees.self) + } + + public func getEmployees(page: Int, perPage: Int, sortField: EmployeeSortField, sortOrder: EmployeeSortOrder) async throws -> Employees { + return try await NetworkService.shared.fetchData(from: EmployeeServiceMode.empty.endpoint, as: Employees.self) + } +} diff --git a/EmployeeDirectory/Services/MockEmployeeService.swift b/EmployeeDirectory/Services/MockEmployeeService.swift index a6697a1..b0bb553 100644 --- a/EmployeeDirectory/Services/MockEmployeeService.swift +++ b/EmployeeDirectory/Services/MockEmployeeService.swift @@ -27,24 +27,14 @@ public class MockEmployeeService: EmployeeServiceProtocol { sortEmployees() } - public func getEmployees(_ serviceMode: EmployeeServiceMode) async throws -> Employees { + public func getEmployees() async throws -> Employees { return wrapper } - public func getEmployees(_ serviceMode: EmployeeServiceMode = .production, - page: Int, perPage: Int, + public func getEmployees(page: Int, perPage: Int, sortField: EmployeeSortField = .fullName, sortOrder: EmployeeSortOrder = .ascending) async throws -> Employees { - - switch serviceMode { - case .malformed: - throw NetworkServiceError.decodingError(DecodingError.dataCorrupted(.init(codingPath: .init(), debugDescription: "Error decoding"))) - case .empty: - return .init(employees: [], total: 0, page: page, perPage: perPage) - default : - break - } - + //resort mock data if sortField != self.sortField || sortOrder != self.sortOrder { self.sortField = sortField diff --git a/EmployeeDirectory/ViewControllers/EmployeesViewController.swift b/EmployeeDirectory/ViewControllers/EmployeesViewController.swift index 2c3ff6a..3ccf1c6 100644 --- a/EmployeeDirectory/ViewControllers/EmployeesViewController.swift +++ b/EmployeeDirectory/ViewControllers/EmployeesViewController.swift @@ -60,7 +60,7 @@ class EmployeesViewController: UIViewController { } } - //Snapshot Handling + /// Snapshot Handling private func applySnapshot(employees: [Employee]) { var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([0]) @@ -143,7 +143,8 @@ class EmployeesViewController: UIViewController { self?.activityIndicator.startAnimating() } else { self?.activityIndicator.stopAnimating() - self?.tableView.refreshControl?.endRefreshing() // End refresh control + self?.tableView.refreshControl?.endRefreshing() + self?.updateFooter() } } .store(in: &cancellables) @@ -250,6 +251,6 @@ extension EmployeesViewController { case 2: selectedMode = .empty default: return } - viewModel.changeMode(to: selectedMode) + viewModel.changeService(to: selectedMode.service) } } diff --git a/EmployeeDirectory/ViewModels/EmployeesViewModel.swift b/EmployeeDirectory/ViewModels/EmployeesViewModel.swift index f3e93c7..8cffd2f 100644 --- a/EmployeeDirectory/ViewModels/EmployeesViewModel.swift +++ b/EmployeeDirectory/ViewModels/EmployeesViewModel.swift @@ -12,8 +12,10 @@ import Combine /// specifically with the EmployeesViewController. @MainActor public class EmployeesViewModel: ObservableObject { + private var serviceMode: EmployeeServiceMode = .production private var employeeService: EmployeeServiceProtocol = MockEmployeeService.shared + @Published public private(set) var employees: [Employee] = [] @Published public private(set) var errorMessage: String? = nil @Published public private(set) var isLoading: Bool = false @@ -51,7 +53,7 @@ public class EmployeesViewModel: ObservableObject { Task { do { // Fetch employees using the paginated API - let wrapper = try await employeeService.getEmployees(serviceMode, page: page, + let wrapper = try await employeeService.getEmployees(page: page, perPage: perPage, sortField: sortField, sortOrder: sortOrder) @@ -81,12 +83,6 @@ public class EmployeesViewModel: ObservableObject { fetchEmployees(page: currentPage + 1) } - /// Change the service mode (e.g., production, malformed, empty) - public func changeMode(to mode: EmployeeServiceMode) { - serviceMode = mode - resetAndFetchEmployees() - } - /// Resets the current employee list and fetches data from page 1 private func resetAndFetchEmployees() { currentPage = 1 @@ -94,4 +90,9 @@ public class EmployeesViewModel: ObservableObject { hasMorePages = true fetchEmployees(page: 1) } + + public func changeService(to employeeService: EmployeeServiceProtocol) { + self.employeeService = employeeService + resetAndFetchEmployees() + } }