Compare commits
No commits in common. "code-pairing-2" and "main" have entirely different histories.
code-pairi
...
main
@ -27,7 +27,7 @@ public enum EmployeeType: String, Codable, CustomStringConvertible {
|
|||||||
/// Employee Object
|
/// Employee Object
|
||||||
/// JSON Object defintion
|
/// JSON Object defintion
|
||||||
/// - https://square.github.io/microsite/mobile-interview-project/
|
/// - https://square.github.io/microsite/mobile-interview-project/
|
||||||
public struct Employee: Hashable, Codable {
|
public struct Employee: Codable {
|
||||||
|
|
||||||
/// The unique identifier for the employee. Represented as a UUID.
|
/// The unique identifier for the employee. Represented as a UUID.
|
||||||
public let uuid: UUID
|
public let uuid: UUID
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
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() async throws -> Employees
|
func getEmployees(_ serviceMode: EmployeeServiceMode) async throws -> Employees
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,13 +25,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
let navigationController = UINavigationController(rootViewController: employeeListVC)
|
let navigationController = UINavigationController(rootViewController: employeeListVC)
|
||||||
window.rootViewController = navigationController
|
window.rootViewController = navigationController
|
||||||
|
|
||||||
let appVersion = 1.5// get app version
|
|
||||||
let currentVersion = 1.0 //cached version
|
|
||||||
|
|
||||||
if appVersion != currentVersion {
|
|
||||||
EmployeeCacheService.shared.clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the window to the scene
|
// Set the window to the scene
|
||||||
self.window = window
|
self.window = window
|
||||||
window.makeKeyAndVisible()
|
window.makeKeyAndVisible()
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
//
|
|
||||||
// EmployeeCacheService.swift
|
|
||||||
// EmployeeDirectory
|
|
||||||
//
|
|
||||||
// Created by Matt Bruce on 2/6/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/// A service that handles image caching using memory, disk, and network in priority order.
|
|
||||||
public class EmployeeCacheService {
|
|
||||||
// MARK: - Properties
|
|
||||||
public static let shared = EmployeeCacheService() // Default shared instance
|
|
||||||
|
|
||||||
/// Memory cache for storing images in RAM.
|
|
||||||
private let emplyoees: Employees? = nil
|
|
||||||
|
|
||||||
/// File manager for handling disk operations.
|
|
||||||
private let fileManager = FileManager.default
|
|
||||||
|
|
||||||
/// Directory where cached images are stored on disk.
|
|
||||||
private let cacheDirectory: URL = {
|
|
||||||
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
|
||||||
}()
|
|
||||||
|
|
||||||
// MARK: - Initializer
|
|
||||||
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public func save(from employees: Employees) throws {
|
|
||||||
let data = try JSONEncoder().encode(employees)
|
|
||||||
try data.write(to: cacheDirectory.appendingPathComponent("employees.json"))
|
|
||||||
}
|
|
||||||
|
|
||||||
public func load() throws -> Employees {
|
|
||||||
let data = try Data(contentsOf: cacheDirectory.appendingPathComponent("employees.json"))
|
|
||||||
return try JSONDecoder().decode(Employees.self, from: data)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func clear() {
|
|
||||||
try? FileManager.default.removeItem(at: cacheDirectory.appendingPathComponent("employees.json"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,22 +7,11 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// These are the testing URL Endpoints for different states
|
/// These are the testing URL Endpoints for different states
|
||||||
internal enum EmployeeServiceMode: String, CaseIterable {
|
public 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 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 {
|
||||||
@ -36,14 +25,8 @@ internal enum EmployeeServiceMode: String, CaseIterable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Service Layer for Employees
|
/// Service Layer for Employees
|
||||||
public class EmployeeService: EmployeeServiceProtocol {
|
public class EmployeeService: EmployeeServiceProtocol {
|
||||||
public enum EmployeeServiceError: Error {
|
|
||||||
case error
|
|
||||||
//case LoadError
|
|
||||||
//case other(Error)
|
|
||||||
}
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
public static let shared = EmployeeService() // Default shared instance
|
public static let shared = EmployeeService() // Default shared instance
|
||||||
|
|
||||||
@ -56,67 +39,7 @@ 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() async throws -> Employees {
|
public func getEmployees(_ serviceMode: EmployeeServiceMode = .production) async throws -> Employees {
|
||||||
var employees: Employees
|
return try await NetworkService.shared.fetchData(from: serviceMode.endpoint, as: Employees.self)
|
||||||
let cache = EmployeeCacheService.shared
|
|
||||||
do {
|
|
||||||
employees = try cache.load()
|
|
||||||
return employees
|
|
||||||
} catch {
|
|
||||||
if let networkFound = try? await fetchNetworkEmployees() {
|
|
||||||
employees = networkFound
|
|
||||||
return employees
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw EmployeeServiceError.error
|
|
||||||
}
|
|
||||||
|
|
||||||
public func fetchNetworkEmployees() async throws -> Employees {
|
|
||||||
var employees: Employees
|
|
||||||
if let networkFound = try? await NetworkService.shared.fetchData(from: EmployeeServiceMode.production.endpoint, as: Employees.self) {
|
|
||||||
employees = networkFound
|
|
||||||
try? EmployeeCacheService.shared.save(from: employees)
|
|
||||||
return employees
|
|
||||||
} else {
|
|
||||||
throw EmployeeServiceError.error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,104 +0,0 @@
|
|||||||
//
|
|
||||||
// EmployeeDetailViewController.swift
|
|
||||||
// EmployeeDirectory
|
|
||||||
//
|
|
||||||
// Created by Matt Bruce on 2/6/25.
|
|
||||||
//
|
|
||||||
import UIKit
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
public class EmployeeDetailViewController: UIViewController {
|
|
||||||
|
|
||||||
private let photoImageView = UIImageView()
|
|
||||||
private let nameLabel = UILabel()
|
|
||||||
private let emailLabel = UILabel()
|
|
||||||
private let teamLabel = UILabel()
|
|
||||||
private let employeeTypeLabel = UILabel()
|
|
||||||
private let phoneLabel = UILabel()
|
|
||||||
private let bioLabel = UILabel()
|
|
||||||
|
|
||||||
private let stackView = UIStackView()
|
|
||||||
|
|
||||||
/// Used for grabbing the photo
|
|
||||||
private var largerPhotoSubscriber: AnyCancellable?
|
|
||||||
|
|
||||||
public var viewModel: EmployeeDetailViewModel?
|
|
||||||
|
|
||||||
public override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
view.backgroundColor = .white
|
|
||||||
|
|
||||||
// Configure Photo
|
|
||||||
photoImageView.image = UIImage(systemName: "person.crop.circle")
|
|
||||||
photoImageView.contentMode = .scaleAspectFit
|
|
||||||
photoImageView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
|
|
||||||
// Configure StackView
|
|
||||||
stackView.axis = .vertical
|
|
||||||
stackView.spacing = 10
|
|
||||||
stackView.alignment = .leading
|
|
||||||
stackView.distribution = .fill
|
|
||||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
|
|
||||||
// Configure Labels
|
|
||||||
bioLabel.numberOfLines = 0
|
|
||||||
|
|
||||||
// Add views to stackView
|
|
||||||
stackView.addArrangedSubview(photoImageView)
|
|
||||||
stackView.addArrangedSubview(nameLabel)
|
|
||||||
stackView.addArrangedSubview(teamLabel)
|
|
||||||
stackView.addArrangedSubview(employeeTypeLabel)
|
|
||||||
stackView.addArrangedSubview(phoneLabel)
|
|
||||||
stackView.addArrangedSubview(emailLabel)
|
|
||||||
stackView.addArrangedSubview(bioLabel)
|
|
||||||
|
|
||||||
view.addSubview(stackView)
|
|
||||||
photoImageView.heightAnchor.constraint(equalToConstant: 200).isActive = true
|
|
||||||
photoImageView.widthAnchor.constraint(equalToConstant: 200).isActive = true
|
|
||||||
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
|
|
||||||
stackView.bottomAnchor.constraint(lessThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
|
|
||||||
stackView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10).isActive = true
|
|
||||||
stackView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10).isActive = true
|
|
||||||
|
|
||||||
// Bind the image to the photoImageView
|
|
||||||
largerPhotoSubscriber = viewModel?.$largePhoto
|
|
||||||
.compactMap { $0 }
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] image in
|
|
||||||
self?.photoImageView.image = image
|
|
||||||
}
|
|
||||||
|
|
||||||
if let viewModel {
|
|
||||||
// Bind data to UI components
|
|
||||||
nameLabel.text = viewModel.fullName
|
|
||||||
emailLabel.text = viewModel.emailAddress
|
|
||||||
teamLabel.text = viewModel.team
|
|
||||||
employeeTypeLabel.text = viewModel.employeeType
|
|
||||||
phoneLabel.text = viewModel.phoneNumber
|
|
||||||
bioLabel.text = viewModel.biography
|
|
||||||
|
|
||||||
// Dynamically show or hide elements based on their content
|
|
||||||
phoneLabel.isHidden = viewModel.phoneNumber == nil
|
|
||||||
bioLabel.isHidden = viewModel.biography == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let phoneTap = UITapGestureRecognizer(target: self, action: #selector(didTapPhoneView(_:)))
|
|
||||||
phoneLabel.isUserInteractionEnabled = true
|
|
||||||
phoneLabel.addGestureRecognizer(phoneTap)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func didTapPhoneView(_ sender: UITapGestureRecognizer) {
|
|
||||||
guard let phoneNumber = viewModel?.phoneNumber?.filter({ $0.isNumber }) else {
|
|
||||||
print("phoneNumber not there")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let numberUrl = URL(string: "tel://\(phoneNumber)")!
|
|
||||||
if UIApplication.shared.canOpenURL(numberUrl) {
|
|
||||||
UIApplication.shared.open(numberUrl)
|
|
||||||
}
|
|
||||||
print("call:", phoneNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -31,49 +31,26 @@ class EmployeesViewController: UIViewController {
|
|||||||
/// Will show specific state of the viewModel
|
/// Will show specific state of the viewModel
|
||||||
private var footerView: TableFooterView?
|
private var footerView: TableFooterView?
|
||||||
|
|
||||||
private var dataSource: UITableViewDiffableDataSource<Int, Employee>!
|
|
||||||
|
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
|
|
||||||
public override func viewDidLoad() {
|
public override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
setupUI()
|
setupUI()
|
||||||
setupDataSource()
|
|
||||||
bindViewModel()
|
bindViewModel()
|
||||||
viewModel.fetchEmployees()
|
viewModel.fetchEmployees()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private Methods
|
// MARK: - Private Methods
|
||||||
|
|
||||||
|
|
||||||
/// Setup TableView dataSource
|
|
||||||
private func setupDataSource() {
|
|
||||||
dataSource = UITableViewDiffableDataSource<Int, Employee>(tableView: tableView) { tableView, indexPath, employee in
|
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: EmployeeTableViewCell.identifier,for: indexPath) as? EmployeeTableViewCell else {
|
|
||||||
return UITableViewCell()
|
|
||||||
}
|
|
||||||
cell.configure(with: EmployeeCellViewModel(employee: employee))
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Snapshot Handling
|
|
||||||
private func applySnapshot(employees: [Employee]) {
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Int, Employee>()
|
|
||||||
snapshot.appendSections([0])
|
|
||||||
snapshot.appendItems(employees, toSection: 0)
|
|
||||||
dataSource.apply(snapshot, animatingDifferences: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Setup the UI by adding the views to the main view
|
/// Setup the UI by adding the views to the main view
|
||||||
private func setupUI() {
|
private func setupUI() {
|
||||||
view.backgroundColor = .white
|
view.backgroundColor = .white
|
||||||
|
|
||||||
// Configure TableView
|
// Configure TableView
|
||||||
tableView.register(EmployeeTableViewCell.self, forCellReuseIdentifier: EmployeeTableViewCell.identifier)
|
tableView.register(EmployeeTableViewCell.self, forCellReuseIdentifier: EmployeeTableViewCell.identifier)
|
||||||
|
tableView.dataSource = self
|
||||||
view.addSubview(tableView)
|
view.addSubview(tableView)
|
||||||
tableView.frame = view.bounds
|
tableView.frame = view.bounds
|
||||||
tableView.delegate = self
|
|
||||||
|
|
||||||
//add pull to refresh
|
//add pull to refresh
|
||||||
tableView.refreshControl = UIRefreshControl()
|
tableView.refreshControl = UIRefreshControl()
|
||||||
@ -93,8 +70,10 @@ class EmployeesViewController: UIViewController {
|
|||||||
private func bindViewModel() {
|
private func bindViewModel() {
|
||||||
viewModel.$employees
|
viewModel.$employees
|
||||||
.receive(on: RunLoop.main)
|
.receive(on: RunLoop.main)
|
||||||
.sink { [weak self] employees in
|
.sink { [weak self] _ in
|
||||||
self?.applySnapshot(employees: employees)
|
self?.updateFooter()
|
||||||
|
self?.tableView.reloadData()
|
||||||
|
self?.tableView.refreshControl?.endRefreshing()
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
@ -105,8 +84,6 @@ class EmployeesViewController: UIViewController {
|
|||||||
self?.activityIndicator.startAnimating()
|
self?.activityIndicator.startAnimating()
|
||||||
} else {
|
} else {
|
||||||
self?.activityIndicator.stopAnimating()
|
self?.activityIndicator.stopAnimating()
|
||||||
self?.tableView.refreshControl?.endRefreshing()
|
|
||||||
self?.updateFooter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@ -141,15 +118,6 @@ class EmployeesViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension EmployeesViewController: UITableViewDelegate{
|
|
||||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
|
||||||
let employee = viewModel.employees[indexPath.row]
|
|
||||||
let details = EmployeeDetailViewController()
|
|
||||||
details.viewModel = .init(employee: employee)
|
|
||||||
navigationController?.pushViewController(details, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark: - Objective-C Methods
|
// Mark: - Objective-C Methods
|
||||||
extension EmployeesViewController {
|
extension EmployeesViewController {
|
||||||
|
|
||||||
@ -168,6 +136,22 @@ extension EmployeesViewController {
|
|||||||
case 2: selectedMode = .empty
|
case 2: selectedMode = .empty
|
||||||
default: return
|
default: return
|
||||||
}
|
}
|
||||||
viewModel.changeService(to: selectedMode.service)
|
viewModel.changeMode(to: selectedMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark: - UITableViewDataSource
|
||||||
|
extension EmployeesViewController: UITableViewDataSource {
|
||||||
|
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
return viewModel.employees.count
|
||||||
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: EmployeeTableViewCell.identifier, for: indexPath) as? EmployeeTableViewCell else {
|
||||||
|
return UITableViewCell()
|
||||||
|
}
|
||||||
|
let employee = viewModel.employees[indexPath.row]
|
||||||
|
cell.configure(with: EmployeeCellViewModel(employee: employee))
|
||||||
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,50 +0,0 @@
|
|||||||
//
|
|
||||||
// EmployeeDetailsViewModel.swift
|
|
||||||
// EmployeeDirectory
|
|
||||||
//
|
|
||||||
// Created by Matt Bruce on 2/6/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
/// ViewModel that will be used along with the EmployeeTableViewCell.
|
|
||||||
@MainActor
|
|
||||||
public class EmployeeDetailViewModel: ObservableObject {
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
private let employee: Employee
|
|
||||||
|
|
||||||
public private(set) var uuid: String
|
|
||||||
public private(set) var fullName: String
|
|
||||||
public private(set) var phoneNumber: String?
|
|
||||||
public private(set) var emailAddress: String
|
|
||||||
public private(set) var biography: String?
|
|
||||||
public private(set) var team: String
|
|
||||||
public private(set) var employeeType: String
|
|
||||||
@Published public private(set) var largePhoto: UIImage?
|
|
||||||
|
|
||||||
// MARK: - Initializer
|
|
||||||
|
|
||||||
public init(employee: Employee) {
|
|
||||||
self.employee = employee
|
|
||||||
|
|
||||||
// Initialize properties
|
|
||||||
uuid = employee.uuid.uuidString
|
|
||||||
fullName = employee.fullName
|
|
||||||
phoneNumber = employee.phoneNumber?.formatUSNumber()
|
|
||||||
emailAddress = employee.emailAddress
|
|
||||||
biography = employee.biography
|
|
||||||
team = employee.team
|
|
||||||
employeeType = employee.employeeType.description
|
|
||||||
|
|
||||||
// Fetch the image for the url if it exists
|
|
||||||
if let endpoint = employee.photoURLLarge {
|
|
||||||
Task{
|
|
||||||
if let photoURL = URL(string: endpoint) {
|
|
||||||
largePhoto = await ImageCacheService.shared.loadImage(from: photoURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,7 +11,7 @@ import Foundation
|
|||||||
/// specifically with the EmployeesViewController.
|
/// specifically with the EmployeesViewController.
|
||||||
@MainActor
|
@MainActor
|
||||||
public class EmployeesViewModel: ObservableObject {
|
public class EmployeesViewModel: ObservableObject {
|
||||||
private var employeeService: EmployeeServiceProtocol = EmployeeService()
|
private var serviceMode: EmployeeServiceMode = .production
|
||||||
|
|
||||||
@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
|
||||||
@ -27,7 +27,7 @@ public class EmployeesViewModel: ObservableObject {
|
|||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
// Fetch employees using the async method
|
// Fetch employees using the async method
|
||||||
let wrapper = try await employeeService.getEmployees()
|
let wrapper = try await EmployeeService.shared.getEmployees(serviceMode)
|
||||||
|
|
||||||
// Update published properties
|
// Update published properties
|
||||||
employees = wrapper.employees
|
employees = wrapper.employees
|
||||||
@ -43,8 +43,8 @@ public class EmployeesViewModel: ObservableObject {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func changeService(to employeeService: EmployeeServiceProtocol) {
|
public func changeMode(to mode: EmployeeServiceMode) {
|
||||||
self.employeeService = employeeService
|
serviceMode = mode
|
||||||
fetchEmployees()
|
fetchEmployees()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,7 +128,6 @@ public class EmployeeTableViewCell: UITableViewCell {
|
|||||||
|
|
||||||
// Bind the image to the photoImageView
|
// Bind the image to the photoImageView
|
||||||
smallPhotoSubscriber = viewModel.$smallPhoto
|
smallPhotoSubscriber = viewModel.$smallPhoto
|
||||||
.compactMap { $0 }
|
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] image in
|
.sink { [weak self] image in
|
||||||
self?.photoImageView.image = image
|
self?.photoImageView.image = image
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user