결과물!

ViewController.swift
import UIKit
import SnapKit
class ViewController: UIViewController {
private var dataSource = [ForecastWeather]()
private let urlQueryItems: [URLQueryItem] = [
URLQueryItem(name: "lat", value: "37.5"),
URLQueryItem(name: "lon", value: "126.9"),
URLQueryItem(name: "appid", value: "91fa5e13abcadd8fcfa36ccfd2d445e4"),
URLQueryItem(name: "units", value: "metric")
]
private let titleLabel: UILabel = {
let label = UILabel()
label.text = "서울특별시"
label.textColor = .white
label.font = .boldSystemFont(ofSize: 30)
return label
}()
private let tempLabel: UILabel = {
let label = UILabel()
label.text = "20도"
label.textColor = .white
label.font = .boldSystemFont(ofSize: 50)
return label
}()
private let tempMinLabel: UILabel = {
let label = UILabel()
label.text = "20도"
label.textColor = .white
label.font = .boldSystemFont(ofSize: 20)
return label
}()
private let tempMaxLabel: UILabel = {
let label = UILabel()
label.text = "20도"
label.textColor = .white
label.font = .boldSystemFont(ofSize: 20)
return label
}()
private let tempStackView: UIStackView = {
let stackview = UIStackView()
stackview.axis = .horizontal
stackview.spacing = 20
stackview.distribution = .fillEqually
return stackview
}()
private let imageView: UIImageView = {
let imageview = UIImageView()
imageview.contentMode = .scaleAspectFit
imageview.backgroundColor = .black
return imageview
}()
private lazy var tableView: UITableView = {
let tableview = UITableView()
tableview.backgroundColor = .black
tableview.delegate = self
tableview.dataSource = self
tableview.register(TableViewCell.self, forCellReuseIdentifier: TableViewCell.id)
return tableview
}()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
fetchCurrentWeatherData()
fetchForecastData()
}
private func fetchData <T: Decodable>(url: URL, completion: @escaping (T?) -> Void) {
let session = URLSession(configuration: .default)
session.dataTask(with: URLRequest(url: url)) { data, response, error in
guard let data, error == nil else {
print("데이터 로드 실패")
completion(nil)
return
}
let successRange = 200..<300
if let response = response as? HTTPURLResponse, successRange.contains(response.statusCode) {
guard let decodedData = try? JSONDecoder().decode(T.self, from: data) else {
print("Json 디코딩 실패")
completion(nil)
return
}
completion(decodedData)
} else {
print("응답 오류")
completion(nil)
}
}.resume()
}
private func fetchCurrentWeatherData() {
var urlComponents = URLComponents(string:"https://api.openweathermap.org/data/2.5/weather")
urlComponents?.queryItems = self.urlQueryItems
guard let url = urlComponents?.url else {
print("잘못된 URL")
return
}
fetchData(url: url) { [weak self] (result: CurrentWeatherResult?) in
guard let self, let result else {return}
DispatchQueue.main.async {
self.tempLabel.text = "\(Int(result.main.temp))°C"
self.tempMinLabel.text = "최저: \(Int(result.main.tempMin))°C"
self.tempMaxLabel.text = "최고: \(Int(result.main.tempMax))°C"
}
guard let imageUrl = URL(string: "https://openweathermap.org/img/wn/\(result.weather[0].icon)@2x.png") else {
return
}
if let data = try? Data(contentsOf: imageUrl) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self.imageView.image = image
}
}
}
}
}
private func fetchForecastData() {
var urlComponents = URLComponents(string: "https://api.openweathermap.org/data/2.5/forecast")
urlComponents?.queryItems = self.urlQueryItems
guard let url = urlComponents?.url else {
print("잘못된 URL")
return
}
fetchData(url: url) { [weak self] (result: ForecastWeatherResult?) in
guard let self, let result else { return }
for forecastWeather in result.list {
print("\(forecastWeather.main) \(forecastWeather.dtTxt)")
}
DispatchQueue.main.async {
self.dataSource = result.list
self.tableView.reloadData()
}
}
}
private func configureUI() {
view.backgroundColor = .black
[titleLabel, tempLabel, tempStackView, imageView, tableView].forEach{ view.addSubview($0) }
[tempMinLabel, tempMaxLabel].forEach{ tempStackView.addArrangedSubview($0) }
titleLabel.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.top.equalToSuperview().offset(120)
}
tempLabel.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.top.equalTo(titleLabel.snp.bottom).offset(10)
}
tempStackView.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.top.equalTo(tempLabel.snp.bottom).offset(10)
}
imageView.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.height.equalTo(160)
$0.top.equalTo(tempStackView.snp.bottom).offset(20)
}
tableView.snp.makeConstraints {
$0.top.equalTo(imageView.snp.bottom).offset(30)
$0.leading.trailing.equalToSuperview().inset(20)
$0.bottom.equalToSuperview().inset(50)
}
}
}
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
40
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell.id) as? TableViewCell else {
return UITableViewCell()
}
cell.configureCell(forecastWeather: dataSource[indexPath.row])
return cell
}
}
TableViewCell.swift
import UIKit
final class TableViewCell: UITableViewCell {
static let id = "TableViewCell"
private let dtTxtLabel : UILabel = {
let label = UILabel()
label.backgroundColor = .black
label.textColor = .white
return label
}()
private let tempLabel : UILabel = {
let label = UILabel()
label.backgroundColor = .black
label.textColor = .white
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configureUI() {
contentView.backgroundColor = .black
[dtTxtLabel, tempLabel].forEach{ contentView.addSubview($0) }
dtTxtLabel.snp.makeConstraints {
$0.centerY.equalToSuperview()
$0.leading.equalToSuperview().inset(20)
}
tempLabel.snp.makeConstraints {
$0.centerY.equalToSuperview()
$0.trailing.equalToSuperview().inset(20)
}
}
public func configureCell(forecastWeather: ForecastWeather) {
dtTxtLabel.text = "\(forecastWeather.dtTxt)"
tempLabel.text = "\(forecastWeather.main.temp)°C"
}
}
CurrentWeatherResult.swift
import Foundation
struct CurrentWeatherResult: Codable {
let weather: [Weather]
let main: WeatherMain
}
struct Weather: Codable {
let id: Int
let main: String
let description: String
let icon: String
}
struct WeatherMain: Codable {
let temp: Double
let tempMin: Double
let tempMax: Double
enum CodingKeys: String, CodingKey {
case temp
case tempMin = "temp_min"
case tempMax = "temp_max"
}
}
ForecastWeatherResult.swift
import Foundation
struct ForecastWeatherResult: Codable {
let list: [ForecastWeather]
}
struct ForecastWeather: Codable {
let main: WeatherMain
let dtTxt: String
enum CodingKeys: String, CodingKey {
case main
case dtTxt = "dt_txt"
}
}