Merge branch 'develop' into requestBuilder

# Conflicts:
#	EmployeeDirectory/Protocols/EmployeeServiceProtocol.swift
#	EmployeeDirectory/ViewControllers/EmployeesViewController.swift
#	EmployeeDirectory/ViewModels/EmployeesViewModel.swift

Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
Matt Bruce 2025-01-21 14:30:22 -06:00
commit 67b2163134
5 changed files with 80 additions and 32 deletions

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -60,7 +60,7 @@ class EmployeesViewController: UIViewController {
}
}
//Snapshot Handling
/// Snapshot Handling
private func applySnapshot(employees: [Employee]) {
var snapshot = NSDiffableDataSourceSnapshot<Int, Employee>()
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)
}
}

View File

@ -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()
}
}