diff --git a/Assignment.xcodeproj/project.xcworkspace/xcuserdata/abhinavkumar.xcuserdatad/UserInterfaceState.xcuserstate b/Assignment.xcodeproj/project.xcworkspace/xcuserdata/abhinavkumar.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..73ff627 Binary files /dev/null and b/Assignment.xcodeproj/project.xcworkspace/xcuserdata/abhinavkumar.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..b0c2178 --- /dev/null +++ b/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcschemes/xcschememanagement.plist b/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..ec91b33 --- /dev/null +++ b/Assignment.xcodeproj/xcuserdata/abhinavkumar.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Assignment.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Assignment/api/ApiService.swift b/Assignment/api/ApiService.swift index 46662a1..95acd44 100644 --- a/Assignment/api/ApiService.swift +++ b/Assignment/api/ApiService.swift @@ -7,27 +7,48 @@ import Foundation -class ApiService : NSObject { +class ApiService { private let baseUrl = "" private let sourcesURL = URL(string: "https://api.restful-api.dev/objects")! - func fetchDeviceDetails(completion : @escaping ([DeviceData]) -> ()){ - URLSession.shared.dataTask(with: sourcesURL) { (data, urlResponse, error) in + func fetchDeviceDetails(completion : @escaping (Result<[DeviceData], Error>) -> Void) { + URLSession.shared.dataTask(with: sourcesURL) { data, response, error in if let error = error { print("Network error: \(error.localizedDescription)") - completion([]) // Return an empty array on network failure + DispatchQueue.main.async{ + completion(.failure(error)) + } + // Return an empty array on network failure return } - if let data = data { - let jsonDecoder = JSONDecoder() - let empData = try! jsonDecoder.decode([DeviceData].self, from: data) - if (empData.isEmpty) { - completion([]) - // Error + + guard let data = data else { + DispatchQueue.main.async{ + completion(.success([])) + + } + return + } + do { + let devices = try JSONDecoder().decode([DeviceData].self, from: data) + DispatchQueue.main.async{ + completion(.success(devices)) + } + } catch{ + print("decode error") + DispatchQueue.main.async{ + completion(.failure(error)) + // let jsonDecoder = JSONDecoder() + // let empData = try! jsonDecoder.decode([DeviceData].self, from: data) + // if (empData.isEmpty) { + // completion([]) + // // Error + // }} } } - }.resume() + } .resume() + } } diff --git a/Assignment/ui/ContentView.swift b/Assignment/ui/ContentView.swift index 253924a..bab4840 100644 --- a/Assignment/ui/ContentView.swift +++ b/Assignment/ui/ContentView.swift @@ -8,11 +8,15 @@ import UIKit -class ContentViewController: UIViewController { +class ContentViewController: UIViewController{ + + private let viewModel = ContentViewModel() private var devices: [DeviceData] = [] + private var filteredDevices: [DeviceData] = [] private var tableView: UITableView! + private let searchController = UISearchController(searchResultsController: nil) private var activityIndicator: UIActivityIndicatorView! override func viewDidLoad() { @@ -38,46 +42,79 @@ class ContentViewController: UIViewController { activityIndicator.center = self.view.center activityIndicator.hidesWhenStopped = true view.addSubview(activityIndicator) - + //api data fetch + setupSearchBar() fetchData() navigationItem.title = "Computers" view.backgroundColor = .white + } func fetchData() { activityIndicator.startAnimating() - - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - if let data = self.viewModel.data { - self.devices = data - self.tableView.reloadData() - } - self.activityIndicator.stopAnimating() +// +// DispatchQueue.main.asyncAfter(deadline: .now() + 1) { +// if let data = self.viewModel.data { +// self.devices = data +// print(data) +// +// self.tableView.reloadData() +// } + viewModel.fetchDevices{ [weak self] devices in + guard let self = self else { return} + self.devices = devices + self.tableView.reloadData() + self.activityIndicator.stopAnimating() self.tableView.isHidden = false } } + private var isFiltering:Bool{ + searchController.isActive && + !(searchController.searchBar.text?.isEmpty ?? true) + } + private func setupSearchBar(){ + searchController.searchResultsUpdater = self + searchController.obscuresBackgroundDuringPresentation = false + searchController.searchBar.placeholder = "Search Devices" + navigationItem.searchController = searchController + navigationItem.hidesSearchBarWhenScrolling = false + definesPresentationContext = true + } } extension ContentViewController: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return devices.count + return isFiltering ? filteredDevices.count: devices.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "DeviceCell", for: indexPath) - let device = devices[indexPath.row] + let device = isFiltering ? filteredDevices[indexPath.row] + :devices[indexPath.row] cell.textLabel?.text = device.name return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let selectedDevice = devices[indexPath.row] - _ = DetailViewController(device: selectedDevice) + let device = isFiltering ? filteredDevices[indexPath.row] + :devices[indexPath.row] + let detailVC = DetailViewController(device: device) + navigationController?.pushViewController(detailVC, animated: true) + } } - +extension ContentViewController: UISearchResultsUpdating{ +func updateSearchResults(for searchController: UISearchController) { + let searchText = searchController.searchBar.text ?? "" + filteredDevices = devices.filter{ + $0.name.lowercased().contains(searchText.lowercased()) + } + tableView.reloadData() + + } +} diff --git a/Assignment/vm/ContentViewModel.swift b/Assignment/vm/ContentViewModel.swift index b4d8326..7f3d562 100644 --- a/Assignment/vm/ContentViewModel.swift +++ b/Assignment/vm/ContentViewModel.swift @@ -8,19 +8,28 @@ import Foundation -class ContentViewModel : ObservableObject { +class ContentViewModel { private let apiService = ApiService() - @Published var navigateDetail: DeviceData? = nil - var data: [DeviceData]? = [] +// @Published var navigateDetail: DeviceData? = nil +// var data: [DeviceData]? = [] + private var devices: [DeviceData] = [] - func fetchAPI() { - apiService.fetchDeviceDetails(completion: { item in - self.data = item - }) - } - func navigateToDetail(navigateDetail: DeviceData) { - self.navigateDetail = navigateDetail + func fetchDevices(completion:@escaping ([DeviceData]) -> Void){ + apiService.fetchDeviceDetails{ result in + switch result { + case.success(let devices): + completion(devices) + case .failure(let error): + print("error",error.localizedDescription) + completion([]) + } + } + } } -} + +// func navigateToDetail(navigateDetail: DeviceData) { +// self.navigateDetail = navigateDetail +// } +