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:
commit
67b2163134
@ -8,8 +8,7 @@
|
|||||||
/// This will be the interface for the API for Employees
|
/// This will be the interface for the API for Employees
|
||||||
public protocol EmployeeServiceProtocol {
|
public protocol EmployeeServiceProtocol {
|
||||||
/// This will get a list of all employees
|
/// This will get a list of all employees
|
||||||
/// - Parameter serviceMode: Mode in which to hit.
|
|
||||||
/// - Returns: An Employees struct
|
/// - Returns: An Employees struct
|
||||||
func getEmployees(_ serviceMode: EmployeeServiceMode) async throws -> Employees
|
func getEmployees() async throws -> Employees
|
||||||
func getEmployees(_ serviceMode: EmployeeServiceMode, page: Int, perPage: Int, sortField: EmployeeSortField, sortOrder: EmployeeSortOrder) async throws -> Employees
|
func getEmployees(page: Int, perPage: Int, sortField: EmployeeSortField, sortOrder: EmployeeSortOrder) async throws -> Employees
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,11 +18,23 @@ public enum EmployeeSortOrder: String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// These are the testing URL Endpoints for different states
|
/// These are the testing URL Endpoints for different states
|
||||||
public enum EmployeeServiceMode: String, CaseIterable {
|
internal enum EmployeeServiceMode: String, CaseIterable {
|
||||||
case production
|
case production
|
||||||
case malformed
|
case malformed
|
||||||
case empty
|
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.
|
/// Enpoint in which to grabe employees from.
|
||||||
public var endpoint: String {
|
public var endpoint: String {
|
||||||
switch self {
|
switch self {
|
||||||
@ -50,8 +62,8 @@ public class EmployeeService: EmployeeServiceProtocol {
|
|||||||
/// This will get a list of all employees
|
/// This will get a list of all employees
|
||||||
/// - Parameter serviceMode: Mode in which to hit.
|
/// - Parameter serviceMode: Mode in which to hit.
|
||||||
/// - Returns: An Employees struct
|
/// - Returns: An Employees struct
|
||||||
public func getEmployees(_ serviceMode: EmployeeServiceMode = .production) async throws -> Employees {
|
public func getEmployees() async throws -> Employees {
|
||||||
return try await NetworkService.shared.fetchData(from: serviceMode.endpoint, as: Employees.self)
|
return try await NetworkService.shared.fetchData(from: EmployeeServiceMode.production.endpoint, as: Employees.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch employees with pagination support
|
/// Fetch employees with pagination support
|
||||||
@ -59,12 +71,11 @@ public class EmployeeService: EmployeeServiceProtocol {
|
|||||||
/// - page: The page number to fetch.
|
/// - page: The page number to fetch.
|
||||||
/// - perPage: The number of employees per page.
|
/// - perPage: The number of employees per page.
|
||||||
/// - Returns: A paginated Employees object.
|
/// - Returns: A paginated Employees object.
|
||||||
public func getEmployees(_ serviceMode: EmployeeServiceMode = .production,
|
public func getEmployees(page: Int, perPage: Int,
|
||||||
page: Int, perPage: Int,
|
|
||||||
sortField: EmployeeSortField = .fullName,
|
sortField: EmployeeSortField = .fullName,
|
||||||
sortOrder: EmployeeSortOrder = .ascending) async throws -> Employees {
|
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
|
throw NetworkServiceError.invalidURL
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,3 +95,49 @@ public class EmployeeService: EmployeeServiceProtocol {
|
|||||||
return try await NetworkService.shared.fetchData(with: request, as: Employees.self)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -27,24 +27,14 @@ public class MockEmployeeService: EmployeeServiceProtocol {
|
|||||||
sortEmployees()
|
sortEmployees()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getEmployees(_ serviceMode: EmployeeServiceMode) async throws -> Employees {
|
public func getEmployees() async throws -> Employees {
|
||||||
return wrapper
|
return wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getEmployees(_ serviceMode: EmployeeServiceMode = .production,
|
public func getEmployees(page: Int, perPage: Int,
|
||||||
page: Int, perPage: Int,
|
|
||||||
sortField: EmployeeSortField = .fullName,
|
sortField: EmployeeSortField = .fullName,
|
||||||
sortOrder: EmployeeSortOrder = .ascending) async throws -> Employees {
|
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
|
//resort mock data
|
||||||
if sortField != self.sortField || sortOrder != self.sortOrder {
|
if sortField != self.sortField || sortOrder != self.sortOrder {
|
||||||
self.sortField = sortField
|
self.sortField = sortField
|
||||||
|
|||||||
@ -60,7 +60,7 @@ class EmployeesViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Snapshot Handling
|
/// Snapshot Handling
|
||||||
private func applySnapshot(employees: [Employee]) {
|
private func applySnapshot(employees: [Employee]) {
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Int, Employee>()
|
var snapshot = NSDiffableDataSourceSnapshot<Int, Employee>()
|
||||||
snapshot.appendSections([0])
|
snapshot.appendSections([0])
|
||||||
@ -143,7 +143,8 @@ class EmployeesViewController: UIViewController {
|
|||||||
self?.activityIndicator.startAnimating()
|
self?.activityIndicator.startAnimating()
|
||||||
} else {
|
} else {
|
||||||
self?.activityIndicator.stopAnimating()
|
self?.activityIndicator.stopAnimating()
|
||||||
self?.tableView.refreshControl?.endRefreshing() // End refresh control
|
self?.tableView.refreshControl?.endRefreshing()
|
||||||
|
self?.updateFooter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@ -250,6 +251,6 @@ extension EmployeesViewController {
|
|||||||
case 2: selectedMode = .empty
|
case 2: selectedMode = .empty
|
||||||
default: return
|
default: return
|
||||||
}
|
}
|
||||||
viewModel.changeMode(to: selectedMode)
|
viewModel.changeService(to: selectedMode.service)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,8 +12,10 @@ import Combine
|
|||||||
/// specifically with the EmployeesViewController.
|
/// specifically with the EmployeesViewController.
|
||||||
@MainActor
|
@MainActor
|
||||||
public class EmployeesViewModel: ObservableObject {
|
public class EmployeesViewModel: ObservableObject {
|
||||||
|
|
||||||
private var serviceMode: EmployeeServiceMode = .production
|
private var serviceMode: EmployeeServiceMode = .production
|
||||||
private var employeeService: EmployeeServiceProtocol = MockEmployeeService.shared
|
private var employeeService: EmployeeServiceProtocol = MockEmployeeService.shared
|
||||||
|
|
||||||
@Published public private(set) var employees: [Employee] = []
|
@Published public private(set) var employees: [Employee] = []
|
||||||
@Published public private(set) var errorMessage: String? = nil
|
@Published public private(set) var errorMessage: String? = nil
|
||||||
@Published public private(set) var isLoading: Bool = false
|
@Published public private(set) var isLoading: Bool = false
|
||||||
@ -51,7 +53,7 @@ public class EmployeesViewModel: ObservableObject {
|
|||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
// Fetch employees using the paginated API
|
// 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,
|
perPage: perPage,
|
||||||
sortField: sortField,
|
sortField: sortField,
|
||||||
sortOrder: sortOrder)
|
sortOrder: sortOrder)
|
||||||
@ -81,12 +83,6 @@ public class EmployeesViewModel: ObservableObject {
|
|||||||
fetchEmployees(page: currentPage + 1)
|
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
|
/// Resets the current employee list and fetches data from page 1
|
||||||
private func resetAndFetchEmployees() {
|
private func resetAndFetchEmployees() {
|
||||||
currentPage = 1
|
currentPage = 1
|
||||||
@ -94,4 +90,9 @@ public class EmployeesViewModel: ObservableObject {
|
|||||||
hasMorePages = true
|
hasMorePages = true
|
||||||
fetchEmployees(page: 1)
|
fetchEmployees(page: 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func changeService(to employeeService: EmployeeServiceProtocol) {
|
||||||
|
self.employeeService = employeeService
|
||||||
|
resetAndFetchEmployees()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user