├── MVVMExample
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── 20.png
│ │ ├── 29.png
│ │ ├── 40.png
│ │ ├── 58.png
│ │ ├── 60.png
│ │ ├── 76.png
│ │ ├── 80.png
│ │ ├── 87.png
│ │ ├── 1024.png
│ │ ├── 120.png
│ │ ├── 152.png
│ │ ├── 167.png
│ │ ├── 180.png
│ │ └── Contents.json
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── primaryColor.colorset
│ │ └── Contents.json
│ └── secondaryColor.colorset
│ │ └── Contents.json
├── Screens
│ └── Employees
│ │ ├── ViewModel
│ │ ├── EmployeeCellViewModel.swift
│ │ └── EmployeesViewModel.swift
│ │ ├── Model
│ │ └── Employee.swift
│ │ └── View
│ │ ├── Cell
│ │ ├── EmployeeCell.swift
│ │ └── EmployeeCell.xib
│ │ └── EmployeesViewController.swift
├── Services
│ └── EmployeesService.swift
├── AppDelegate.swift
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── Helpers
│ └── HttpRequestHelper.swift
└── SceneDelegate.swift
├── README.md
├── MVVMExample.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── johncodeos.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── johncodeos.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
└── demo.json
/MVVMExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/20.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/76.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/152.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/167.png
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # MVVMiOSExample
3 | Implement MVVM pattern with Swift in iOS
4 |
5 | https://johncodeos.com/how-to-implement-mvvm-pattern-with-swift-in-ios/
6 |
--------------------------------------------------------------------------------
/MVVMExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/MVVMExample.xcodeproj/xcuserdata/johncodeos.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/MVVMExample.xcodeproj/project.xcworkspace/xcuserdata/johncodeos.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/HEAD/MVVMExample.xcodeproj/project.xcworkspace/xcuserdata/johncodeos.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/MVVMExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MVVMExample/Screens/Employees/ViewModel/EmployeeCellViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmployeeCellViewModel.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import Foundation
9 |
10 | struct EmployeeCellViewModel {
11 | var id: String
12 | var name: String
13 | var salary: String
14 | var age: String
15 | }
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/primaryColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.294",
9 | "green" : "0.133",
10 | "red" : "0.737"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/secondaryColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.251",
9 | "green" : "0.110",
10 | "red" : "0.620"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/MVVMExample.xcodeproj/xcuserdata/johncodeos.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MVVMExample.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MVVMExample/Screens/Employees/Model/Employee.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Employee.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import Foundation
9 |
10 | typealias Employees = [Employee]
11 |
12 | // MARK: - Employee
13 | struct Employee: Codable {
14 | let id: String
15 | let employeeName: String
16 | let employeeSalary: String
17 | let employeeAge: String
18 |
19 | enum CodingKeys: String, CodingKey {
20 | case id
21 | case employeeName = "employee_name"
22 | case employeeSalary = "employee_salary"
23 | case employeeAge = "employee_age"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/MVVMExample/Services/EmployeesService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmployeesService.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import Foundation
9 |
10 | protocol EmployeesServiceProtocol {
11 | func getEmployees(completion: @escaping (_ success: Bool, _ results: Employees?, _ error: String?) -> ())
12 | }
13 |
14 | class EmployeesService: EmployeesServiceProtocol {
15 | func getEmployees(completion: @escaping (Bool, Employees?, String?) -> ()) {
16 | HttpRequestHelper().GET(url: "https://raw.githubusercontent.com/johncodeos-blog/MVVMiOSExample/main/demo.json", params: ["": ""], httpHeader: .application_json) { success, data in
17 | if success {
18 | do {
19 | let model = try JSONDecoder().decode(Employees.self, from: data!)
20 | completion(true, model, nil)
21 | } catch {
22 | completion(false, nil, "Error: Trying to parse Employees to model")
23 | }
24 | } else {
25 | completion(false, nil, "Error: Employees GET Request failed")
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/MVVMExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
13 | // Override point for customization after application launch.
14 | return true
15 | }
16 |
17 | // MARK: UISceneSession Lifecycle
18 |
19 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
20 | // Called when a new scene session is being created.
21 | // Use this method to select a configuration to create the new scene with.
22 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
23 | }
24 |
25 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
26 | // Called when the user discards a scene session.
27 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
28 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/MVVMExample/Screens/Employees/View/Cell/EmployeeCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmployeeCell.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import UIKit
9 |
10 | class EmployeeCell: UITableViewCell {
11 | @IBOutlet weak var idLabel: UILabel!
12 | @IBOutlet weak var nameLabel: UILabel!
13 | @IBOutlet weak var salaryLabel: UILabel!
14 | @IBOutlet weak var ageLabel: UILabel!
15 |
16 | static var identifier: String { return String(describing: self) }
17 | static var nib: UINib { return UINib(nibName: identifier, bundle: nil) }
18 |
19 | var cellViewModel: EmployeeCellViewModel? {
20 | didSet {
21 | idLabel.text = cellViewModel?.id
22 | nameLabel.text = cellViewModel?.name
23 | salaryLabel.text = cellViewModel?.salary
24 | ageLabel.text = cellViewModel?.age
25 | }
26 | }
27 |
28 | override func awakeFromNib() {
29 | super.awakeFromNib()
30 | initView()
31 | }
32 |
33 | func initView() {
34 | // Cell view customization
35 | backgroundColor = .clear
36 |
37 | // Line separator full width
38 | preservesSuperviewLayoutMargins = false
39 | separatorInset = UIEdgeInsets.zero
40 | layoutMargins = UIEdgeInsets.zero
41 | }
42 |
43 | override func prepareForReuse() {
44 | super.prepareForReuse()
45 | idLabel.text = nil
46 | nameLabel.text = nil
47 | salaryLabel.text = nil
48 | ageLabel.text = nil
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/MVVMExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/MVVMExample/Screens/Employees/ViewModel/EmployeesViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmployeesViewModel.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import Foundation
9 |
10 | class EmployeesViewModel: NSObject {
11 | private var employeeService: EmployeesServiceProtocol
12 |
13 | var reloadTableView: (() -> Void)?
14 |
15 | var employees = Employees()
16 |
17 | var employeeCellViewModels = [EmployeeCellViewModel]() {
18 | didSet {
19 | reloadTableView?()
20 | }
21 | }
22 |
23 | init(employeeService: EmployeesServiceProtocol = EmployeesService()) {
24 | self.employeeService = employeeService
25 | }
26 |
27 | func getEmployees() {
28 | employeeService.getEmployees { success, model, error in
29 | if success, let employees = model {
30 | self.fetchData(employees: employees)
31 | } else {
32 | print(error!)
33 | }
34 | }
35 | }
36 |
37 | func fetchData(employees: Employees) {
38 | self.employees = employees // Cache
39 | var vms = [EmployeeCellViewModel]()
40 | for employee in employees {
41 | vms.append(createCellModel(employee: employee))
42 | }
43 | employeeCellViewModels = vms
44 | }
45 |
46 | func createCellModel(employee: Employee) -> EmployeeCellViewModel {
47 | let id = employee.id
48 | let name = employee.employeeName
49 | let salary = "$ " + employee.employeeSalary
50 | let age = employee.employeeAge
51 |
52 | return EmployeeCellViewModel(id: id, name: name, salary: salary, age: age)
53 | }
54 |
55 | func getCellViewModel(at indexPath: IndexPath) -> EmployeeCellViewModel {
56 | return employeeCellViewModels[indexPath.row]
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/MVVMExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UIApplicationSupportsIndirectInputEvents
43 |
44 | UILaunchStoryboardName
45 | LaunchScreen
46 | UIMainStoryboardFile
47 | Main
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UISupportedInterfaceOrientations
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationLandscapeLeft
56 | UIInterfaceOrientationLandscapeRight
57 |
58 | UISupportedInterfaceOrientations~ipad
59 |
60 | UIInterfaceOrientationPortrait
61 | UIInterfaceOrientationPortraitUpsideDown
62 | UIInterfaceOrientationLandscapeLeft
63 | UIInterfaceOrientationLandscapeRight
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/MVVMExample/Helpers/HttpRequestHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HttpRequestHelper.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import Foundation
9 |
10 | enum HTTPHeaderFields {
11 | case application_json
12 | case application_x_www_form_urlencoded
13 | case none
14 | }
15 |
16 | class HttpRequestHelper {
17 | func GET(url: String, params: [String: String], httpHeader: HTTPHeaderFields, complete: @escaping (Bool, Data?) -> ()) {
18 | guard var components = URLComponents(string: url) else {
19 | print("Error: cannot create URLCompontents")
20 | return
21 | }
22 | components.queryItems = params.map { key, value in
23 | URLQueryItem(name: key, value: value)
24 | }
25 |
26 | guard let url = components.url else {
27 | print("Error: cannot create URL")
28 | return
29 | }
30 | var request = URLRequest(url: url)
31 | request.httpMethod = "GET"
32 |
33 | switch httpHeader {
34 | case .application_json:
35 | request.setValue("application/json", forHTTPHeaderField: "Content-Type")
36 | case .application_x_www_form_urlencoded:
37 | request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
38 | case .none: break
39 | }
40 |
41 |
42 | // .ephemeral prevent JSON from caching (They'll store in Ram and nothing on Disk)
43 | let config = URLSessionConfiguration.ephemeral
44 | let session = URLSession(configuration: config)
45 | session.dataTask(with: request) { data, response, error in
46 | guard error == nil else {
47 | print("Error: problem calling GET")
48 | print(error!)
49 | complete(false, nil)
50 | return
51 | }
52 | guard let data = data else {
53 | print("Error: did not receive data")
54 | complete(false, nil)
55 | return
56 | }
57 | guard let response = response as? HTTPURLResponse, (200 ..< 300) ~= response.statusCode else {
58 | print("Error: HTTP request failed")
59 | complete(false, nil)
60 | return
61 | }
62 | complete(true, data)
63 | }.resume()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/MVVMExample/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 | var window: UIWindow?
12 |
13 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
14 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
15 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
16 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
17 | guard let _ = (scene as? UIWindowScene) else { return }
18 | }
19 |
20 | func sceneDidDisconnect(_ scene: UIScene) {
21 | // Called as the scene is being released by the system.
22 | // This occurs shortly after the scene enters the background, or when its session is discarded.
23 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
24 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
25 | }
26 |
27 | func sceneDidBecomeActive(_ scene: UIScene) {
28 | // Called when the scene has moved from an inactive state to an active state.
29 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
30 | }
31 |
32 | func sceneWillResignActive(_ scene: UIScene) {
33 | // Called when the scene will move from an active state to an inactive state.
34 | // This may occur due to temporary interruptions (ex. an incoming phone call).
35 | }
36 |
37 | func sceneWillEnterForeground(_ scene: UIScene) {
38 | // Called as the scene transitions from the background to the foreground.
39 | // Use this method to undo the changes made on entering the background.
40 | }
41 |
42 | func sceneDidEnterBackground(_ scene: UIScene) {
43 | // Called as the scene transitions from the foreground to the background.
44 | // Use this method to save data, release shared resources, and store enough scene-specific state information
45 | // to restore the scene back to its current state.
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/MVVMExample/Screens/Employees/View/EmployeesViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EmployeesViewController.swift
3 | // MVVMExample
4 | //
5 | // Created by John Codeos on 06/19/20.
6 | //
7 |
8 | import UIKit
9 |
10 | class EmployeesViewController: UIViewController {
11 | @IBOutlet weak var tableView: UITableView!
12 |
13 | lazy var viewModel = {
14 | EmployeesViewModel()
15 | }()
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | // Fix navigation bar color in iOS 15 and above
21 | if #available(iOS 15, *) {
22 | let appearance = UINavigationBarAppearance()
23 | appearance.configureWithOpaqueBackground()
24 | appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
25 | appearance.backgroundColor = UIColor(named: "primaryColor")
26 | navigationController?.navigationBar.standardAppearance = appearance
27 | navigationController?.navigationBar.scrollEdgeAppearance = navigationController?.navigationBar.standardAppearance
28 | }
29 |
30 | initView()
31 | initViewModel()
32 | }
33 |
34 | func initView() {
35 | tableView.delegate = self
36 | tableView.dataSource = self
37 | tableView.backgroundColor = UIColor(#colorLiteral(red: 0.6196078431, green: 0.1098039216, blue: 0.2509803922, alpha: 1))
38 | tableView.separatorColor = .white
39 | tableView.separatorStyle = .singleLine
40 | tableView.tableFooterView = UIView()
41 | tableView.allowsSelection = false
42 |
43 | tableView.register(EmployeeCell.nib, forCellReuseIdentifier: EmployeeCell.identifier)
44 | }
45 |
46 | func initViewModel() {
47 | // Get employees data
48 | viewModel.getEmployees()
49 |
50 | // Reload TableView closure
51 | viewModel.reloadTableView = { [weak self] in
52 | DispatchQueue.main.async {
53 | self?.tableView.reloadData()
54 | }
55 | }
56 | }
57 | }
58 |
59 | // MARK: - UITableViewDelegate
60 |
61 | extension EmployeesViewController: UITableViewDelegate {
62 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
63 | return 130
64 | }
65 | }
66 |
67 | // MARK: - UITableViewDataSource
68 |
69 | extension EmployeesViewController: UITableViewDataSource {
70 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
71 | return viewModel.employeeCellViewModels.count
72 | }
73 |
74 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
75 | guard let cell = tableView.dequeueReusableCell(withIdentifier: EmployeeCell.identifier, for: indexPath) as? EmployeeCell else { fatalError("xib does not exists") }
76 | let cellVM = viewModel.getCellViewModel(at: indexPath)
77 | cell.cellViewModel = cellVM
78 | return cell
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/demo.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id": "1",
3 | "employee_name": "Tiger Nixon",
4 | "employee_salary": "320800",
5 | "employee_age": "61"
6 | },
7 | {
8 | "id": "2",
9 | "employee_name": "Garrett Winters",
10 | "employee_salary": "170750",
11 | "employee_age": "63"
12 | },
13 | {
14 | "id": "3",
15 | "employee_name": "Ashton Cox",
16 | "employee_salary": "86000",
17 | "employee_age": "66"
18 | },
19 | {
20 | "id": "4",
21 | "employee_name": "Cedric Kelly",
22 | "employee_salary": "433060",
23 | "employee_age": "22"
24 | },
25 | {
26 | "id": "5",
27 | "employee_name": "Airi Satou",
28 | "employee_salary": "162700",
29 | "employee_age": "33"
30 | },
31 | {
32 | "id": "6",
33 | "employee_name": "Brielle Williamson",
34 | "employee_salary": "372000",
35 | "employee_age": "61"
36 | },
37 | {
38 | "id": "7",
39 | "employee_name": "Herrod Chandler",
40 | "employee_salary": "137500",
41 | "employee_age": "59"
42 | },
43 | {
44 | "id": "8",
45 | "employee_name": "Rhona Davidson",
46 | "employee_salary": "327900",
47 | "employee_age": "55"
48 | },
49 | {
50 | "id": "9",
51 | "employee_name": "Colleen Hurst",
52 | "employee_salary": "205500",
53 | "employee_age": "39"
54 | },
55 | {
56 | "id": "10",
57 | "employee_name": "Sonya Frost",
58 | "employee_salary": "103600",
59 | "employee_age": "23"
60 | },
61 | {
62 | "id": "11",
63 | "employee_name": "Jena Gaines",
64 | "employee_salary": "90560",
65 | "employee_age": "30"
66 | },
67 | {
68 | "id": "12",
69 | "employee_name": "Quinn Flynn",
70 | "employee_salary": "342000",
71 | "employee_age": "22"
72 | },
73 | {
74 | "id": "13",
75 | "employee_name": "Charde Marshall",
76 | "employee_salary": "470600",
77 | "employee_age": "36"
78 | },
79 | {
80 | "id": "14",
81 | "employee_name": "Haley Kennedy",
82 | "employee_salary": "313500",
83 | "employee_age": "43"
84 | },
85 | {
86 | "id": "15",
87 | "employee_name": "Tatyana Fitzpatrick",
88 | "employee_salary": "385750",
89 | "employee_age": "19"
90 | },
91 | {
92 | "id": "16",
93 | "employee_name": "Michael Silva",
94 | "employee_salary": "198500",
95 | "employee_age": "66"
96 | },
97 | {
98 | "id": "17",
99 | "employee_name": "Paul Byrd",
100 | "employee_salary": "725000",
101 | "employee_age": "64"
102 | },
103 | {
104 | "id": "18",
105 | "employee_name": "Gloria Little",
106 | "employee_salary": "237500",
107 | "employee_age": "59"
108 | },
109 | {
110 | "id": "19",
111 | "employee_name": "Bradley Greer",
112 | "employee_salary": "132000",
113 | "employee_age": "41"
114 | },
115 | {
116 | "id": "20",
117 | "employee_name": "Dai Rios",
118 | "employee_salary": "217500",
119 | "employee_age": "35"
120 | },
121 | {
122 | "id": "21",
123 | "employee_name": "Jenette Caldwell",
124 | "employee_salary": "345000",
125 | "employee_age": "30"
126 | },
127 | {
128 | "id": "22",
129 | "employee_name": "Yuri Berry",
130 | "employee_salary": "675000",
131 | "employee_age": "40"
132 | },
133 | {
134 | "id": "23",
135 | "employee_name": "Caesar Vance",
136 | "employee_salary": "106450",
137 | "employee_age": "21"
138 | },
139 | {
140 | "id": "24",
141 | "employee_name": "Doris Wilder",
142 | "employee_salary": "85600",
143 | "employee_age": "23"
144 | }
145 | ]
--------------------------------------------------------------------------------
/MVVMExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "filename" : "40.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "idiom" : "iphone",
11 | "size" : "20x20",
12 | "filename" : "60.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "idiom" : "iphone",
17 | "size" : "29x29",
18 | "filename" : "58.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "idiom" : "iphone",
23 | "size" : "29x29",
24 | "filename" : "87.png",
25 | "scale" : "3x"
26 | },
27 | {
28 | "idiom" : "iphone",
29 | "size" : "40x40",
30 | "filename" : "80.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "40x40",
36 | "filename" : "120.png",
37 | "scale" : "3x"
38 | },
39 | {
40 | "idiom" : "iphone",
41 | "size" : "60x60",
42 | "filename" : "120.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "idiom" : "iphone",
47 | "size" : "60x60",
48 | "filename" : "180.png",
49 | "scale" : "3x"
50 | },
51 | {
52 | "idiom" : "ipad",
53 | "size" : "20x20",
54 | "filename" : "20.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "idiom" : "ipad",
59 | "size" : "20x20",
60 | "filename" : "40.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "29x29",
66 | "filename" : "29.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "idiom" : "ipad",
71 | "size" : "29x29",
72 | "filename" : "58.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "idiom" : "ipad",
77 | "size" : "40x40",
78 | "filename" : "40.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "idiom" : "ipad",
83 | "size" : "40x40",
84 | "filename" : "80.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "idiom" : "ipad",
89 | "size" : "76x76",
90 | "filename" : "76.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "idiom" : "ipad",
95 | "size" : "76x76",
96 | "filename" : "152.png",
97 | "scale" : "2x"
98 | },
99 | {
100 | "idiom" : "ipad",
101 | "size" : "83.5x83.5",
102 | "filename" : "167.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "idiom" : "ios-marketing",
107 | "size" : "1024x1024",
108 | "filename" : "1024.png",
109 | "scale" : "1x"
110 | }
111 | ],
112 | "info" : {
113 | "version" : 1,
114 | "author" : "xcode"
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/MVVMExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/MVVMExample/Screens/Employees/View/Cell/EmployeeCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
60 |
66 |
72 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/MVVMExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | C26BAB1F2573B6F3008C2705 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26BAB1E2573B6F3008C2705 /* AppDelegate.swift */; };
11 | C26BAB212573B6F3008C2705 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26BAB202573B6F3008C2705 /* SceneDelegate.swift */; };
12 | C26BAB232573B6F3008C2705 /* EmployeesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26BAB222573B6F3008C2705 /* EmployeesViewController.swift */; };
13 | C26BAB262573B6F3008C2705 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C26BAB242573B6F3008C2705 /* Main.storyboard */; };
14 | C26BAB282573B6F4008C2705 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C26BAB272573B6F4008C2705 /* Assets.xcassets */; };
15 | C26BAB2B2573B6F4008C2705 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C26BAB292573B6F4008C2705 /* LaunchScreen.storyboard */; };
16 | C26BAB392573C92B008C2705 /* Employee.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26BAB382573C92B008C2705 /* Employee.swift */; };
17 | C26BAB3C2573C93E008C2705 /* EmployeesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26BAB3B2573C93E008C2705 /* EmployeesViewModel.swift */; };
18 | C26BAB412573D4A7008C2705 /* HttpRequestHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26BAB402573D4A7008C2705 /* HttpRequestHelper.swift */; };
19 | C26BAB452573D4FD008C2705 /* EmployeesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C26BAB442573D4FD008C2705 /* EmployeesService.swift */; };
20 | C2DE3FCD2574268A00320D6C /* EmployeeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2DE3FCB2574268A00320D6C /* EmployeeCell.swift */; };
21 | C2DE3FCE2574268A00320D6C /* EmployeeCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C2DE3FCC2574268A00320D6C /* EmployeeCell.xib */; };
22 | C2DE3FD12574338300320D6C /* EmployeeCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2DE3FD02574338300320D6C /* EmployeeCellViewModel.swift */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXFileReference section */
26 | C26BAB1B2573B6F3008C2705 /* MVVMExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MVVMExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
27 | C26BAB1E2573B6F3008C2705 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
28 | C26BAB202573B6F3008C2705 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
29 | C26BAB222573B6F3008C2705 /* EmployeesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmployeesViewController.swift; sourceTree = ""; };
30 | C26BAB252573B6F3008C2705 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
31 | C26BAB272573B6F4008C2705 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
32 | C26BAB2A2573B6F4008C2705 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
33 | C26BAB2C2573B6F4008C2705 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
34 | C26BAB382573C92B008C2705 /* Employee.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Employee.swift; sourceTree = ""; };
35 | C26BAB3B2573C93E008C2705 /* EmployeesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmployeesViewModel.swift; sourceTree = ""; };
36 | C26BAB402573D4A7008C2705 /* HttpRequestHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpRequestHelper.swift; sourceTree = ""; };
37 | C26BAB442573D4FD008C2705 /* EmployeesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmployeesService.swift; sourceTree = ""; };
38 | C2DE3FCB2574268A00320D6C /* EmployeeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmployeeCell.swift; sourceTree = ""; };
39 | C2DE3FCC2574268A00320D6C /* EmployeeCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EmployeeCell.xib; sourceTree = ""; };
40 | C2DE3FD02574338300320D6C /* EmployeeCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmployeeCellViewModel.swift; sourceTree = ""; };
41 | /* End PBXFileReference section */
42 |
43 | /* Begin PBXFrameworksBuildPhase section */
44 | C26BAB182573B6F3008C2705 /* Frameworks */ = {
45 | isa = PBXFrameworksBuildPhase;
46 | buildActionMask = 2147483647;
47 | files = (
48 | );
49 | runOnlyForDeploymentPostprocessing = 0;
50 | };
51 | /* End PBXFrameworksBuildPhase section */
52 |
53 | /* Begin PBXGroup section */
54 | C26BAB122573B6F3008C2705 = {
55 | isa = PBXGroup;
56 | children = (
57 | C26BAB1D2573B6F3008C2705 /* MVVMExample */,
58 | C26BAB1C2573B6F3008C2705 /* Products */,
59 | );
60 | sourceTree = "";
61 | };
62 | C26BAB1C2573B6F3008C2705 /* Products */ = {
63 | isa = PBXGroup;
64 | children = (
65 | C26BAB1B2573B6F3008C2705 /* MVVMExample.app */,
66 | );
67 | name = Products;
68 | sourceTree = "";
69 | };
70 | C26BAB1D2573B6F3008C2705 /* MVVMExample */ = {
71 | isa = PBXGroup;
72 | children = (
73 | C26BAB3F2573D466008C2705 /* Helpers */,
74 | C26BAB3E2573CCF4008C2705 /* Services */,
75 | C2DE3FD32574458300320D6C /* Screens */,
76 | C26BAB1E2573B6F3008C2705 /* AppDelegate.swift */,
77 | C26BAB202573B6F3008C2705 /* SceneDelegate.swift */,
78 | C26BAB242573B6F3008C2705 /* Main.storyboard */,
79 | C26BAB272573B6F4008C2705 /* Assets.xcassets */,
80 | C26BAB292573B6F4008C2705 /* LaunchScreen.storyboard */,
81 | C26BAB2C2573B6F4008C2705 /* Info.plist */,
82 | );
83 | path = MVVMExample;
84 | sourceTree = "";
85 | };
86 | C26BAB332573C83F008C2705 /* View */ = {
87 | isa = PBXGroup;
88 | children = (
89 | C2DE3FCA2574266100320D6C /* Cell */,
90 | C26BAB222573B6F3008C2705 /* EmployeesViewController.swift */,
91 | );
92 | path = View;
93 | sourceTree = "";
94 | };
95 | C26BAB342573C848008C2705 /* ViewModel */ = {
96 | isa = PBXGroup;
97 | children = (
98 | C2DE3FD02574338300320D6C /* EmployeeCellViewModel.swift */,
99 | C26BAB3B2573C93E008C2705 /* EmployeesViewModel.swift */,
100 | );
101 | path = ViewModel;
102 | sourceTree = "";
103 | };
104 | C26BAB352573C858008C2705 /* Model */ = {
105 | isa = PBXGroup;
106 | children = (
107 | C26BAB382573C92B008C2705 /* Employee.swift */,
108 | );
109 | path = Model;
110 | sourceTree = "";
111 | };
112 | C26BAB3E2573CCF4008C2705 /* Services */ = {
113 | isa = PBXGroup;
114 | children = (
115 | C26BAB442573D4FD008C2705 /* EmployeesService.swift */,
116 | );
117 | path = Services;
118 | sourceTree = "";
119 | };
120 | C26BAB3F2573D466008C2705 /* Helpers */ = {
121 | isa = PBXGroup;
122 | children = (
123 | C26BAB402573D4A7008C2705 /* HttpRequestHelper.swift */,
124 | );
125 | path = Helpers;
126 | sourceTree = "";
127 | };
128 | C2DE3FCA2574266100320D6C /* Cell */ = {
129 | isa = PBXGroup;
130 | children = (
131 | C2DE3FCB2574268A00320D6C /* EmployeeCell.swift */,
132 | C2DE3FCC2574268A00320D6C /* EmployeeCell.xib */,
133 | );
134 | path = Cell;
135 | sourceTree = "";
136 | };
137 | C2DE3FD32574458300320D6C /* Screens */ = {
138 | isa = PBXGroup;
139 | children = (
140 | C2DE3FD42574459C00320D6C /* Employees */,
141 | );
142 | path = Screens;
143 | sourceTree = "";
144 | };
145 | C2DE3FD42574459C00320D6C /* Employees */ = {
146 | isa = PBXGroup;
147 | children = (
148 | C26BAB352573C858008C2705 /* Model */,
149 | C26BAB332573C83F008C2705 /* View */,
150 | C26BAB342573C848008C2705 /* ViewModel */,
151 | );
152 | path = Employees;
153 | sourceTree = "";
154 | };
155 | /* End PBXGroup section */
156 |
157 | /* Begin PBXNativeTarget section */
158 | C26BAB1A2573B6F3008C2705 /* MVVMExample */ = {
159 | isa = PBXNativeTarget;
160 | buildConfigurationList = C26BAB2F2573B6F4008C2705 /* Build configuration list for PBXNativeTarget "MVVMExample" */;
161 | buildPhases = (
162 | C26BAB172573B6F3008C2705 /* Sources */,
163 | C26BAB182573B6F3008C2705 /* Frameworks */,
164 | C26BAB192573B6F3008C2705 /* Resources */,
165 | );
166 | buildRules = (
167 | );
168 | dependencies = (
169 | );
170 | name = MVVMExample;
171 | productName = MVVMExample;
172 | productReference = C26BAB1B2573B6F3008C2705 /* MVVMExample.app */;
173 | productType = "com.apple.product-type.application";
174 | };
175 | /* End PBXNativeTarget section */
176 |
177 | /* Begin PBXProject section */
178 | C26BAB132573B6F3008C2705 /* Project object */ = {
179 | isa = PBXProject;
180 | attributes = {
181 | LastSwiftUpdateCheck = 1220;
182 | LastUpgradeCheck = 1220;
183 | TargetAttributes = {
184 | C26BAB1A2573B6F3008C2705 = {
185 | CreatedOnToolsVersion = 12.2;
186 | };
187 | };
188 | };
189 | buildConfigurationList = C26BAB162573B6F3008C2705 /* Build configuration list for PBXProject "MVVMExample" */;
190 | compatibilityVersion = "Xcode 9.3";
191 | developmentRegion = en;
192 | hasScannedForEncodings = 0;
193 | knownRegions = (
194 | en,
195 | Base,
196 | );
197 | mainGroup = C26BAB122573B6F3008C2705;
198 | productRefGroup = C26BAB1C2573B6F3008C2705 /* Products */;
199 | projectDirPath = "";
200 | projectRoot = "";
201 | targets = (
202 | C26BAB1A2573B6F3008C2705 /* MVVMExample */,
203 | );
204 | };
205 | /* End PBXProject section */
206 |
207 | /* Begin PBXResourcesBuildPhase section */
208 | C26BAB192573B6F3008C2705 /* Resources */ = {
209 | isa = PBXResourcesBuildPhase;
210 | buildActionMask = 2147483647;
211 | files = (
212 | C26BAB2B2573B6F4008C2705 /* LaunchScreen.storyboard in Resources */,
213 | C2DE3FCE2574268A00320D6C /* EmployeeCell.xib in Resources */,
214 | C26BAB282573B6F4008C2705 /* Assets.xcassets in Resources */,
215 | C26BAB262573B6F3008C2705 /* Main.storyboard in Resources */,
216 | );
217 | runOnlyForDeploymentPostprocessing = 0;
218 | };
219 | /* End PBXResourcesBuildPhase section */
220 |
221 | /* Begin PBXSourcesBuildPhase section */
222 | C26BAB172573B6F3008C2705 /* Sources */ = {
223 | isa = PBXSourcesBuildPhase;
224 | buildActionMask = 2147483647;
225 | files = (
226 | C26BAB232573B6F3008C2705 /* EmployeesViewController.swift in Sources */,
227 | C26BAB412573D4A7008C2705 /* HttpRequestHelper.swift in Sources */,
228 | C26BAB392573C92B008C2705 /* Employee.swift in Sources */,
229 | C26BAB1F2573B6F3008C2705 /* AppDelegate.swift in Sources */,
230 | C26BAB3C2573C93E008C2705 /* EmployeesViewModel.swift in Sources */,
231 | C2DE3FCD2574268A00320D6C /* EmployeeCell.swift in Sources */,
232 | C26BAB452573D4FD008C2705 /* EmployeesService.swift in Sources */,
233 | C2DE3FD12574338300320D6C /* EmployeeCellViewModel.swift in Sources */,
234 | C26BAB212573B6F3008C2705 /* SceneDelegate.swift in Sources */,
235 | );
236 | runOnlyForDeploymentPostprocessing = 0;
237 | };
238 | /* End PBXSourcesBuildPhase section */
239 |
240 | /* Begin PBXVariantGroup section */
241 | C26BAB242573B6F3008C2705 /* Main.storyboard */ = {
242 | isa = PBXVariantGroup;
243 | children = (
244 | C26BAB252573B6F3008C2705 /* Base */,
245 | );
246 | name = Main.storyboard;
247 | sourceTree = "";
248 | };
249 | C26BAB292573B6F4008C2705 /* LaunchScreen.storyboard */ = {
250 | isa = PBXVariantGroup;
251 | children = (
252 | C26BAB2A2573B6F4008C2705 /* Base */,
253 | );
254 | name = LaunchScreen.storyboard;
255 | sourceTree = "";
256 | };
257 | /* End PBXVariantGroup section */
258 |
259 | /* Begin XCBuildConfiguration section */
260 | C26BAB2D2573B6F4008C2705 /* Debug */ = {
261 | isa = XCBuildConfiguration;
262 | buildSettings = {
263 | ALWAYS_SEARCH_USER_PATHS = NO;
264 | CLANG_ANALYZER_NONNULL = YES;
265 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
267 | CLANG_CXX_LIBRARY = "libc++";
268 | CLANG_ENABLE_MODULES = YES;
269 | CLANG_ENABLE_OBJC_ARC = YES;
270 | CLANG_ENABLE_OBJC_WEAK = YES;
271 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
272 | CLANG_WARN_BOOL_CONVERSION = YES;
273 | CLANG_WARN_COMMA = YES;
274 | CLANG_WARN_CONSTANT_CONVERSION = YES;
275 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
277 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
278 | CLANG_WARN_EMPTY_BODY = YES;
279 | CLANG_WARN_ENUM_CONVERSION = YES;
280 | CLANG_WARN_INFINITE_RECURSION = YES;
281 | CLANG_WARN_INT_CONVERSION = YES;
282 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
283 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
286 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
287 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
288 | CLANG_WARN_STRICT_PROTOTYPES = YES;
289 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
290 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
291 | CLANG_WARN_UNREACHABLE_CODE = YES;
292 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
293 | COPY_PHASE_STRIP = NO;
294 | DEBUG_INFORMATION_FORMAT = dwarf;
295 | ENABLE_STRICT_OBJC_MSGSEND = YES;
296 | ENABLE_TESTABILITY = YES;
297 | GCC_C_LANGUAGE_STANDARD = gnu11;
298 | GCC_DYNAMIC_NO_PIC = NO;
299 | GCC_NO_COMMON_BLOCKS = YES;
300 | GCC_OPTIMIZATION_LEVEL = 0;
301 | GCC_PREPROCESSOR_DEFINITIONS = (
302 | "DEBUG=1",
303 | "$(inherited)",
304 | );
305 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
306 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
307 | GCC_WARN_UNDECLARED_SELECTOR = YES;
308 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
309 | GCC_WARN_UNUSED_FUNCTION = YES;
310 | GCC_WARN_UNUSED_VARIABLE = YES;
311 | IPHONEOS_DEPLOYMENT_TARGET = 14.2;
312 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
313 | MTL_FAST_MATH = YES;
314 | ONLY_ACTIVE_ARCH = YES;
315 | SDKROOT = iphoneos;
316 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
317 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
318 | };
319 | name = Debug;
320 | };
321 | C26BAB2E2573B6F4008C2705 /* Release */ = {
322 | isa = XCBuildConfiguration;
323 | buildSettings = {
324 | ALWAYS_SEARCH_USER_PATHS = NO;
325 | CLANG_ANALYZER_NONNULL = YES;
326 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
327 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
328 | CLANG_CXX_LIBRARY = "libc++";
329 | CLANG_ENABLE_MODULES = YES;
330 | CLANG_ENABLE_OBJC_ARC = YES;
331 | CLANG_ENABLE_OBJC_WEAK = YES;
332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
333 | CLANG_WARN_BOOL_CONVERSION = YES;
334 | CLANG_WARN_COMMA = YES;
335 | CLANG_WARN_CONSTANT_CONVERSION = YES;
336 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
338 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
339 | CLANG_WARN_EMPTY_BODY = YES;
340 | CLANG_WARN_ENUM_CONVERSION = YES;
341 | CLANG_WARN_INFINITE_RECURSION = YES;
342 | CLANG_WARN_INT_CONVERSION = YES;
343 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
344 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
345 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
347 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
348 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
349 | CLANG_WARN_STRICT_PROTOTYPES = YES;
350 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
351 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
352 | CLANG_WARN_UNREACHABLE_CODE = YES;
353 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
354 | COPY_PHASE_STRIP = NO;
355 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
356 | ENABLE_NS_ASSERTIONS = NO;
357 | ENABLE_STRICT_OBJC_MSGSEND = YES;
358 | GCC_C_LANGUAGE_STANDARD = gnu11;
359 | GCC_NO_COMMON_BLOCKS = YES;
360 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
361 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
362 | GCC_WARN_UNDECLARED_SELECTOR = YES;
363 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
364 | GCC_WARN_UNUSED_FUNCTION = YES;
365 | GCC_WARN_UNUSED_VARIABLE = YES;
366 | IPHONEOS_DEPLOYMENT_TARGET = 14.2;
367 | MTL_ENABLE_DEBUG_INFO = NO;
368 | MTL_FAST_MATH = YES;
369 | SDKROOT = iphoneos;
370 | SWIFT_COMPILATION_MODE = wholemodule;
371 | SWIFT_OPTIMIZATION_LEVEL = "-O";
372 | VALIDATE_PRODUCT = YES;
373 | };
374 | name = Release;
375 | };
376 | C26BAB302573B6F4008C2705 /* Debug */ = {
377 | isa = XCBuildConfiguration;
378 | buildSettings = {
379 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
380 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
381 | CODE_SIGN_STYLE = Automatic;
382 | DEVELOPMENT_TEAM = 6P8Q9Q6J62;
383 | INFOPLIST_FILE = MVVMExample/Info.plist;
384 | LD_RUNPATH_SEARCH_PATHS = (
385 | "$(inherited)",
386 | "@executable_path/Frameworks",
387 | );
388 | PRODUCT_BUNDLE_IDENTIFIER = com.example.MVVMExample;
389 | PRODUCT_NAME = "$(TARGET_NAME)";
390 | SWIFT_VERSION = 5.0;
391 | TARGETED_DEVICE_FAMILY = "1,2";
392 | };
393 | name = Debug;
394 | };
395 | C26BAB312573B6F4008C2705 /* Release */ = {
396 | isa = XCBuildConfiguration;
397 | buildSettings = {
398 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
399 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
400 | CODE_SIGN_STYLE = Automatic;
401 | DEVELOPMENT_TEAM = 6P8Q9Q6J62;
402 | INFOPLIST_FILE = MVVMExample/Info.plist;
403 | LD_RUNPATH_SEARCH_PATHS = (
404 | "$(inherited)",
405 | "@executable_path/Frameworks",
406 | );
407 | PRODUCT_BUNDLE_IDENTIFIER = com.example.MVVMExample;
408 | PRODUCT_NAME = "$(TARGET_NAME)";
409 | SWIFT_VERSION = 5.0;
410 | TARGETED_DEVICE_FAMILY = "1,2";
411 | };
412 | name = Release;
413 | };
414 | /* End XCBuildConfiguration section */
415 |
416 | /* Begin XCConfigurationList section */
417 | C26BAB162573B6F3008C2705 /* Build configuration list for PBXProject "MVVMExample" */ = {
418 | isa = XCConfigurationList;
419 | buildConfigurations = (
420 | C26BAB2D2573B6F4008C2705 /* Debug */,
421 | C26BAB2E2573B6F4008C2705 /* Release */,
422 | );
423 | defaultConfigurationIsVisible = 0;
424 | defaultConfigurationName = Release;
425 | };
426 | C26BAB2F2573B6F4008C2705 /* Build configuration list for PBXNativeTarget "MVVMExample" */ = {
427 | isa = XCConfigurationList;
428 | buildConfigurations = (
429 | C26BAB302573B6F4008C2705 /* Debug */,
430 | C26BAB312573B6F4008C2705 /* Release */,
431 | );
432 | defaultConfigurationIsVisible = 0;
433 | defaultConfigurationName = Release;
434 | };
435 | /* End XCConfigurationList section */
436 | };
437 | rootObject = C26BAB132573B6F3008C2705 /* Project object */;
438 | }
439 |
--------------------------------------------------------------------------------