diff --git a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailPageViewController.swift b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailPageViewController.swift index de6307f..cd24571 100644 --- a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailPageViewController.swift +++ b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailPageViewController.swift @@ -109,7 +109,6 @@ class DetailPageViewController: UIViewController { } extension DetailPageViewController: UIPageViewControllerDataSource { - // UIPageViewControllerDataSource λ©”μ„œλ“œ func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { if let currentIndex = detailViewControllers.firstIndex(of: viewController as! DetailViewController), currentIndex > 0 { return detailViewControllers[currentIndex - 1] diff --git a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailViewController.swift b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailViewController.swift index 2d0a3bf..09bf066 100644 --- a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailViewController.swift +++ b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewController/DetailViewController.swift @@ -11,17 +11,7 @@ import SnapKit import Then final class DetailViewController: UIViewController { - var indexNumber: Int = 0 - var minTempArray: [Int] = [] - var maxTempArray: [Int] = [] - var minMinTemp: Int = 0 - var maxMaxTemp: Int = 0 - var hourWeatherCount: Int = 0 - - var hourDetailWeathersData: [WeatherDetailResponseDTO] = [WeatherDetailResponseDTO(cod: "", message: 0, cnt: 0, list: [List(dt: 0, main: MainClass(temp: 0, feelsLike: 0, tempMin: 0, tempMax: 0, pressure: 0, seaLevel: 0, grndLevel: 0, humidity: 0, tempKf: 0), weather: [DetailWeather(id: 0, main: .clear, description: "", icon: "")], clouds: Clouds(all: 0), wind: Wind(speed: 0, deg: 0, gust: 0), visibility: 0, pop: 0, sys: DetailSys(pod: .d), dtTxt: "", rain: Rain(the3h: 0), snow: Rain(the3h: 0))], city: City(id: 0, name: "", coord: Coord(lon: 0, lat: 0), country: "", population: 0, timezone: 0, sunrise: 0, sunset: 0))] - - var detailWeatherData: WeatherResponseDTO = WeatherResponseDTO(coord: Coord(lon: 0, lat: 0), weather: [Weathers(id: 0, main: "", description: "", icon: "")], base: "", main: Main(temp: 0.0, feelsLike: 0.0, tempMin: 0.0, tempMax: 0.0, pressure: 0, humidity: 0, seaLevel: 0, grndLevel: 0), visibility: 0, wind: Wind(speed: 0.0, deg: 0, gust: 0.0), clouds: Clouds(all: 0), dt: 0, sys: Sys(type: 0, id: 0, country: "", sunrise: 0, sunset: 0), timezone: 0, id: 0, name: "", cod: 0) let backgroundImageView = UIImageView() @@ -29,22 +19,31 @@ final class DetailViewController: UIViewController { collectionViewLayout: detailFlowLayout) let detailFlowLayout = UICollectionViewFlowLayout() + let detailViewModel = DetailViewModel() + override func viewDidLoad() { super.viewDidLoad() - loadWeatherDetailData() setUI() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) - loadWeatherDetailData() + updateHourlyWeather() + } + + private func updateHourlyWeather() { + detailViewModel.loadWeatherDetailData { + self.detailCollectionView.reloadData() + } } private func setUI() { - setStyle() - setLayout() - setDelegate() - configCollectionView() + detailViewModel.loadWeatherDetailData { + self.setStyle() + self.setLayout() + self.setDelegate() + self.configCollectionView() + } } private func setDelegate() { @@ -96,19 +95,43 @@ final class DetailViewController: UIViewController { $0.bottom.equalToSuperview().inset(82) } } +} + +extension DetailViewController: UICollectionViewDelegate { } + +extension DetailViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + + switch indexPath.section { + case 0: + if kind == UICollectionView.elementKindSectionHeader { + let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: DetailLocalView.identifier, for: indexPath) as! DetailLocalView + headerView.configLocalView(data: detailViewModel.detailWeatherData) + return headerView + } else { + return UICollectionReusableView() + } + default: + return UICollectionReusableView() + } + } - func caculateMinMax(data: [TenDaysWeather]) { - data.forEach { - minTempArray.append($0.minTemp) - maxTempArray.append($0.maxTemp) + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + if collectionView == detailCollectionView { + switch section { + case 0: + return CGSize(width: UIScreen.main.bounds.width, height: 260) + default: + return CGSize.zero + } + } else { + return CGSize.zero } - - minMinTemp = minTempArray.min() ?? 0 - maxMaxTemp = maxTempArray.max() ?? 0 } } -extension DetailViewController: UICollectionViewDelegate { } +extension DetailViewController: UITableViewDelegate { } + extension DetailViewController: UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { @@ -123,7 +146,7 @@ extension DetailViewController: UICollectionViewDataSource { if collectionView == detailCollectionView { return 1 } else { - return hourWeatherCount + return detailViewModel.hourWeatherCount } } @@ -158,8 +181,7 @@ extension DetailViewController: UICollectionViewDataSource { return cell case 1: guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TenDaysCardView.identifier, for: indexPath) as? TenDaysCardView else { return UICollectionViewCell() } - self.caculateMinMax(data: tenDaysWeatherDummy) - + self.detailViewModel.caculateMinMax(data: tenDaysWeatherDummy) cell.tenDaysTableView.delegate = self cell.tenDaysTableView.dataSource = self return cell @@ -168,45 +190,13 @@ extension DetailViewController: UICollectionViewDataSource { } } else { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailTimeCollectionViewCell.identifier, for: indexPath) as? DetailTimeCollectionViewCell else { return UICollectionViewCell() } - cell.bindData(data: hourDetailWeathersData[indexNumber], row: indexPath.row) + cell.bindData(data: detailViewModel.hourDetailWeathersData[indexNumber], row: indexPath.row) return cell } } } -extension DetailViewController: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - - switch indexPath.section { - case 0: - if kind == UICollectionView.elementKindSectionHeader { - let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: DetailLocalView.identifier, for: indexPath) as! DetailLocalView - headerView.configLocalView(data: detailWeatherData) - return headerView - } else { - return UICollectionReusableView() - } - default: - return UICollectionReusableView() - } - } - - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - if collectionView == detailCollectionView { - switch section { - case 0: - return CGSize(width: UIScreen.main.bounds.width, height: 260) - default: - return CGSize.zero - } - } else { - return CGSize.zero - } - } -} - -extension DetailViewController: UITableViewDelegate { } extension DetailViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 @@ -216,8 +206,8 @@ extension DetailViewController: UITableViewDataSource { guard let cell = tableView.dequeueReusableCell(withIdentifier: TenDaysTableViewCell.identifier, for: indexPath) as? TenDaysTableViewCell else { return UITableViewCell() } cell.bindData(data: tenDaysWeatherDummy[indexPath.row]) cell.selectionStyle = .none - cell.minMinTemp = minMinTemp - cell.maxMaxTemp = maxMaxTemp + cell.minMinTemp = detailViewModel.minMinTemp + cell.maxMaxTemp = detailViewModel.maxMaxTemp return cell } @@ -225,37 +215,3 @@ extension DetailViewController: UITableViewDataSource { return 55 } } - -extension DetailViewController { - private func loadWeatherDetailData() { - Task { - do { - let cities = ["seoul", "daegu", "busan", "daejeon", "mokpo"] - - self.hourDetailWeathersData = [] - var hourDetailWeatherDataArray: [WeatherDetailResponseDTO] = [] - - for cityName in cities { - do { - if let receivedData = try await WeatherDetailService.shared.GetDetailWeatherData(cityName: cityName) { - hourDetailWeatherDataArray.append(receivedData) - } - } catch { - print("Failed to get weather data for \(cityName): \(error)") - } - } - - DispatchQueue.main.async { - self.hourDetailWeathersData = hourDetailWeatherDataArray - self.hourWeatherCount = 9 - self.detailCollectionView.reloadData() - print(hourDetailWeatherDataArray) - print("πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›") - } - } catch { - print(error) - } - } - } -} - diff --git a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewModel/DetaliViewModel.swift b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewModel/DetaliViewModel.swift new file mode 100644 index 0000000..eb38598 --- /dev/null +++ b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Detail/ViewModel/DetaliViewModel.swift @@ -0,0 +1,57 @@ +// +// DetailViewModel.swift +// DO_SOPT_33_Assignment +// +// Created by 정채은 on 12/5/23. +// + +import UIKit + +final class DetailViewModel: NSObject { + + var minTempArray: [Int] = [] + var maxTempArray: [Int] = [] + var minMinTemp: Int = 0 + var maxMaxTemp: Int = 0 + var hourWeatherCount: Int = 0 + + var detailWeatherData: WeatherResponseDTO = WeatherResponseDTO(coord: Coord(lon: 0, lat: 0), weather: [Weathers(id: 0, main: "", description: "", icon: "")], base: "", main: Main(temp: 0.0, feelsLike: 0.0, tempMin: 0.0, tempMax: 0.0, pressure: 0, humidity: 0, seaLevel: 0, grndLevel: 0), visibility: 0, wind: Wind(speed: 0.0, deg: 0, gust: 0.0), clouds: Clouds(all: 0), dt: 0, sys: Sys(type: 0, id: 0, country: "", sunrise: 0, sunset: 0), timezone: 0, id: 0, name: "", cod: 0) + + var hourDetailWeathersData: [WeatherDetailResponseDTO] = [WeatherDetailResponseDTO(cod: "", message: 0, cnt: 0, list: [List(dt: 0, main: MainClass(temp: 0, feelsLike: 0, tempMin: 0, tempMax: 0, pressure: 0, seaLevel: 0, grndLevel: 0, humidity: 0, tempKf: 0), weather: [DetailWeather(id: 0, main: .clear, description: "", icon: "")], clouds: Clouds(all: 0), wind: Wind(speed: 0, deg: 0, gust: 0), visibility: 0, pop: 0, sys: DetailSys(pod: .d), dtTxt: "", rain: Rain(the3h: 0), snow: Rain(the3h: 0))], city: City(id: 0, name: "", coord: Coord(lon: 0, lat: 0), country: "", population: 0, timezone: 0, sunrise: 0, sunset: 0))] + + func loadWeatherDetailData(completion : @escaping() -> Void) { + Task { + do { + let cities = ["seoul", "daegu", "busan", "daejeon", "mokpo"] + + self.hourDetailWeathersData = [] + + for cityName in cities { + do { + if let receivedData = try await WeatherDetailService.shared.GetDetailWeatherData(cityName: cityName) { + hourDetailWeathersData.append(receivedData) + } + } catch { + print("Failed to get weather data for \(cityName): \(error)") + } + } + + DispatchQueue.main.async { + self.hourWeatherCount = 9 + print("πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›") + completion() + } + } + } + } + + func caculateMinMax(data: [TenDaysWeather]) { + data.forEach { + minTempArray.append($0.minTemp) + maxTempArray.append($0.maxTemp) + } + + minMinTemp = minTempArray.min() ?? 0 + maxMaxTemp = maxTempArray.max() ?? 0 + } +} diff --git a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/Button/WeatherListButton.swift b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/Button/WeatherListButton.swift index 1180ca2..cbe0297 100644 --- a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/Button/WeatherListButton.swift +++ b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/Button/WeatherListButton.swift @@ -10,14 +10,9 @@ import UIKit import SnapKit import Then -protocol WeatherButtonDelegate: AnyObject { - func weatherButtonTapped(sender: WeatherListButton) -} - class WeatherListButton: UIButton { var indexNumber = 0 - weak var weatherButtonDelegate: WeatherButtonDelegate? let myPlaceLabel = UILabel() let localLabel = UILabel() @@ -29,13 +24,11 @@ class WeatherListButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) setUI() - setAddTarget() } required init?(coder: NSCoder) { super.init(coder: coder) setUI() - setAddTarget() } private func setUI() { @@ -43,10 +36,6 @@ class WeatherListButton: UIButton { setLayout() } - private func setAddTarget() { - self.addTarget(self, action: #selector(weatherButtonTapped), for: .touchUpInside) - } - func setStyle() { self.setBackgroundImage(UIImage(named: "imgList"), for: .normal) self.imageView?.contentMode = .scaleAspectFill @@ -126,8 +115,4 @@ class WeatherListButton: UIButton { $0.trailing.equalTo(minTempLabel.snp.leading).offset(-6) } } - - @objc func weatherButtonTapped() { - weatherButtonDelegate?.weatherButtonTapped(sender: self) - } } diff --git a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/ViewController/HomeViewController.swift b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/ViewController/HomeViewController.swift index 4717330..e33cdaa 100644 --- a/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/ViewController/HomeViewController.swift +++ b/DO_SOPT_33_Assignment/DO_SOPT_33_Assignment/Presentation/Home/ViewController/HomeViewController.swift @@ -12,9 +12,6 @@ import Then final class HomeViewController: UIViewController { - var resultArray: [WeatherResponseDTO] = [] - var mainWeathersData: [WeatherResponseDTO] = [] - let moreButton = UIButton() let weatherTitleLabel = UILabel() let searchBar = UISearchBar() @@ -23,9 +20,13 @@ final class HomeViewController: UIViewController { collectionViewLayout: homeFlowLayout) private let homeFlowLayout = UICollectionViewFlowLayout() + var homeViewModel = HomeViewModel() + override func viewDidLoad() { super.viewDidLoad() setUI() + bindViewModel() + setDelegate() } override func viewWillAppear(_ animated: Bool) { @@ -39,11 +40,20 @@ final class HomeViewController: UIViewController { setCollectionViewConfig() } + private func setDelegate() { + homeViewModel.homeSearchBarDelegate = self + } + + private func bindViewModel() { + self.homeCollectionView.dataSource = homeViewModel + self.searchBar.delegate = homeViewModel + self.homeViewModel.weatherButtonDelegate = self + } + private func setCollectionViewConfig() { self.homeCollectionView.register(HomeWeatherCollectionViewCell.self, forCellWithReuseIdentifier: HomeWeatherCollectionViewCell.identifier) self.homeCollectionView.delegate = self - self.homeCollectionView.dataSource = self } private func setStyle() { @@ -71,8 +81,6 @@ final class HomeViewController: UIViewController { // μœ„μ•„λž˜λ‘œ μƒκΈ°λŠ” μ„  μ‚­μ œ $0.barTintColor = .clear - - $0.delegate = self } homeCollectionView.do { @@ -119,46 +127,8 @@ final class HomeViewController: UIViewController { } } -extension HomeViewController: WeatherButtonDelegate { - - func weatherButtonTapped(sender: WeatherListButton) { - let detailPageViewController = DetailPageViewController() - - for index in 0.., with event: UIEvent?){ self.view.endEditing(true) } -} - -extension HomeViewController: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return resultArray.count - } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeWeatherCollectionViewCell.identifier, for: indexPath) as? HomeWeatherCollectionViewCell else { return UICollectionViewCell() } - cell.weatherButton.weatherButtonDelegate = self - cell.bindData(data: resultArray[indexPath.row]) - cell.weatherButton.indexNumber = indexPath.row - return cell + private func loadWeatherData() { + Task { + let success = await homeViewModel.loadWeatherData() + if success { + self.homeCollectionView.reloadData() + print("πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›") + } + } } } extension HomeViewController: UICollectionViewDelegate { } -extension HomeViewController { - private func loadWeatherData() { - Task { - do { - let cities = ["seoul", "daegu", "busan", "daejeon", "mokpo"] - - self.mainWeathersData = [] - var weatherDataArray: [WeatherResponseDTO] = [] - - for cityName in cities { - do { - if let receivedData = try await WeatherService.shared.GetWeatherData(cityName: cityName) { - weatherDataArray.append(receivedData) - } - } catch { - print("Failed to get weather data for \(cityName): \(error)") - } - } - - DispatchQueue.main.async { - self.mainWeathersData = weatherDataArray - self.resultArray = self.mainWeathersData - self.homeCollectionView.reloadData() - print("πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›πŸ’›") - } - } catch { - print(error) - } +extension HomeViewController: WeatherButtonDelegate { + + func weatherButtonTapped(sender: WeatherListButton) { + let detailPageViewController = DetailPageViewController() + + for index in 0.. Bool { + do { + let cities = ["seoul", "daegu", "busan", "daejeon", "mokpo"] + + self.mainWeathersData = [] + var weatherDataArray: [WeatherResponseDTO] = [] + + for cityName in cities { + do { + if let receivedData = try await WeatherService.shared.GetWeatherData(cityName: cityName) { + weatherDataArray.append(receivedData) + } + } catch { + print("Failed to get weather data for \(cityName): \(error)") + } + } + self.mainWeathersData = weatherDataArray + self.resultArray = self.mainWeathersData + return true + } + } +} + +extension HomeViewModel: UISearchBarDelegate { + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + guard let text = searchBar.text else { return } + + self.resultArray = [] + + mainWeathersData.forEach { + // λŒ€μ†Œλ¬Έμž ꡬ별은 μ•ˆλ¨.. + if $0.name.contains(text) { + resultArray.append($0) + } + } + + print(resultArray) + + if text.isEmpty { + self.resultArray = mainWeathersData + } + + homeSearchBarDelegate?.updateData() + } +} + + +extension HomeViewModel: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return resultArray.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeWeatherCollectionViewCell.identifier, for: indexPath) as? HomeWeatherCollectionViewCell else { return UICollectionViewCell() } + cell.weatherButton.addTarget(self, action: #selector(weatherButtonTapped), for: .touchUpInside) + cell.bindData(data: resultArray[indexPath.row]) + cell.weatherButton.indexNumber = indexPath.row + return cell + } +} + +extension HomeViewModel { + @objc func weatherButtonTapped(sender: WeatherListButton) { + weatherButtonDelegate?.weatherButtonTapped(sender: sender) + } +}