Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

날씨앱 [STEP 1] caron #36

Open
wants to merge 9 commits into
base: rft_2_caron
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
73B64D442BAB32DD0076ADE0 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73B64D432BAB32DD0076ADE0 /* Utils.swift */; };
C741F6702B58F00500A4DDC0 /* Weather.swift in Sources */ = {isa = PBXBuildFile; fileRef = C741F66F2B58F00500A4DDC0 /* Weather.swift */; };
C7743D8D2B21C38100DF0D09 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */; };
C7743D8F2B21C38100DF0D09 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8E2B21C38100DF0D09 /* SceneDelegate.swift */; };
Expand All @@ -19,6 +20,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
73B64D432BAB32DD0076ADE0 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
C741F66F2B58F00500A4DDC0 /* Weather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weather.swift; sourceTree = "<group>"; };
C7743D892B21C38100DF0D09 /* WeatherForecast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeatherForecast.app; sourceTree = BUILT_PRODUCTS_DIR; };
C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -68,6 +70,7 @@
C7743D902B21C38100DF0D09 /* ViewController.swift */,
C741F66F2B58F00500A4DDC0 /* Weather.swift */,
C7743DA22B21CA8500DF0D09 /* WeatherDetailViewController.swift */,
73B64D432BAB32DD0076ADE0 /* Utils.swift */,
C7743D922B21C38100DF0D09 /* Main.storyboard */,
C7743D952B21C38200DF0D09 /* Assets.xcassets */,
C7743D972B21C38200DF0D09 /* LaunchScreen.storyboard */,
Expand Down Expand Up @@ -150,6 +153,7 @@
C7743DA12B21C3B400DF0D09 /* WeatherTableViewCell.swift in Sources */,
C7743D912B21C38100DF0D09 /* ViewController.swift in Sources */,
C7743D8D2B21C38100DF0D09 /* AppDelegate.swift in Sources */,
73B64D442BAB32DD0076ADE0 /* Utils.swift in Sources */,
C7743DA32B21CA8600DF0D09 /* WeatherDetailViewController.swift in Sources */,
C741F6702B58F00500A4DDC0 /* Weather.swift in Sources */,
C7743D8F2B21C38100DF0D09 /* SceneDelegate.swift in Sources */,
Expand Down
28 changes: 28 additions & 0 deletions WeatherForecast/WeatherForecast/Utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Utils.swift
// WeatherForecast
//
// Created by qussk on 3/21/24.
//

import UIKit


final class Utils {
static func dateSetUp(_ format: String?) -> DateFormatter {
let formatter: DateFormatter = DateFormatter()
let dateFormat = DateFormat(dataFormater: format, dateFormatStyle: .none)
formatter.timeStyle = .short
formatter.locale = .init(identifier: dateFormat.locale)
formatter.dateFormat = dateFormat.dataFormater

guard format != nil else {
formatter.dateFormat = .none
return formatter
}

return formatter
}

}

150 changes: 104 additions & 46 deletions WeatherForecast/WeatherForecast/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,52 @@

import UIKit

class ViewController: UIViewController {
var tableView: UITableView!
let refreshControl: UIRefreshControl = UIRefreshControl()
var weatherJSON: WeatherJSON?
var icons: [UIImage]?
let imageChache: NSCache<NSString, UIImage> = NSCache()
let dateFormatter: DateFormatter = {
let formatter: DateFormatter = DateFormatter()
formatter.locale = .init(identifier: "ko_KR")
formatter.dateFormat = "yyyy-MM-dd(EEEEE) a HH:mm"
return formatter
}()

final class ViewController: UIViewController {

private var tableView: UITableView!
private let refreshControl: UIRefreshControl = UIRefreshControl()
private var weatherJSON: WeatherJSON?
private var icons: [UIImage]?
private let imageChache: NSCache<NSString, UIImage> = NSCache()

var tempUnit: TempUnit = .metric
private var tempUnit: TempUnit = .metric

override func viewDidLoad() {
super.viewDidLoad()
initialSetUp()
}
}

fileprivate enum WeatherTitleType: String {
case celsius = "섭씨"
case fahrenheit = "화씨"
}

//fileprivate enum TempValueUnit: Double {
// case metric, imperial
// var expression: Double {
// switch self {
// case .metric: return "℃"
// case .imperial: return celsiusToFahrenheit(<#T##celsius: Double##Double#>)
// }
// }
//
// private func celsiusToFahrenheit(_ celsius: Double) -> Double {
// return celsius * 9 / 5 + 32
// }
//}

extension ViewController {

@objc private func changeTempUnit() {
switch tempUnit {
case .imperial:
case .imperial: //화
tempUnit = .metric
navigationItem.rightBarButtonItem?.title = "섭씨"
case .metric:
navigationItem.rightBarButtonItem?.title = "\(WeatherTitleType.celsius.rawValue)"
case .metric: //섭
tempUnit = .imperial
navigationItem.rightBarButtonItem?.title = "화씨"
navigationItem.rightBarButtonItem?.title = "\(WeatherTitleType.fahrenheit.rawValue)"
}
refresh()
}
Expand All @@ -46,8 +62,22 @@ extension ViewController {
refreshControl.endRefreshing()
}

private func temperatureValue(_ celsius: Double, teampUnit: TempUnit) -> String {
guard tempUnit == .metric
else {
return "\(celsius)"
}

let fahrenheitTemperature = celsiusToFahrenheit(celsius)
return "\(fahrenheitTemperature)"
}

private func celsiusToFahrenheit(_ celsius: Double) -> Double {
return celsius * 9 / 5 + 32
}

private func initialSetUp() {
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "화씨", image: nil, target: self, action: #selector(changeTempUnit))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "\(WeatherTitleType.fahrenheit)", image: nil, target: self, action: #selector(changeTempUnit))

layTable()

Expand Down Expand Up @@ -117,36 +147,14 @@ extension ViewController: UITableViewDataSource {
let weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] else {
return cell
}

let weather = weatherForecastInfo.weather
let tempValue = temperatureValue(weatherForecastInfo.main.temp, teampUnit: tempUnit)

cell.weatherLabel.text = weatherForecastInfo.weather.main
cell.descriptionLabel.text = weatherForecastInfo.weather.description
cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)"

let date: Date = Date(timeIntervalSince1970: weatherForecastInfo.dt)
cell.dateLabel.text = dateFormatter.string(from: date)

let iconName: String = weatherForecastInfo.weather.icon
let urlString: String = "https://openweathermap.org/img/wn/\(iconName)@2x.png"
weatherTableCell(cell: cell, indexPath: indexPath,iconName: weather.icon, imageView: cell.weatherIcon)

if let image = imageChache.object(forKey: urlString as NSString) {
cell.weatherIcon.image = image
return cell
}

Task {
guard let url: URL = URL(string: urlString),
let (data, _) = try? await URLSession.shared.data(from: url),
let image: UIImage = UIImage(data: data) else {
return
}

imageChache.setObject(image, forKey: urlString as NSString)

if indexPath == tableView.indexPath(for: cell) {
cell.weatherIcon.image = image
}
}

cell.configure(weatherIcon: cell.weatherIcon, dateLabel: dataTimeIntervalSince1970(weatherForecastInfo.dt), temperatureLabel: "\(tempValue)\(tempUnit.expression)", weatherLabel: weather.main, descriptionLabel: weather.description)

return cell
}
}
Expand All @@ -162,5 +170,55 @@ extension ViewController: UITableViewDelegate {
navigationController?.show(detailViewController, sender: self)
}
}
extension ViewController {
private func dataTimeIntervalSince1970(_ dt: TimeInterval) -> String {
let date: Date = Date(timeIntervalSince1970: dt)
return Utils.dateSetUp(DataCase.long).string(from: date)
}
}

extension ViewController {
private func weatherTableCell(cell: WeatherTableViewCell, indexPath: IndexPath, iconName: String, imageView: UIImageView) {
let urlString: String = "\(ImageURLType.path.rawValue)\(iconName)\(ImageURLType.png.rawValue)"


setImageChache(chache: imageChache, cell: cell, urlString: urlString)


setImageTask(table: tableView, index: indexPath, cell: cell, urlString: urlString)

}

private func setImageChache(chache: NSCache<NSString, UIImage>, cell: WeatherTableViewCell, urlString: String) {
if let image = chache.object(forKey: urlString as NSString) {
cell.weatherIcon.image = image
return
}
}

private func setImageTask(table: UITableView, index: IndexPath, cell: WeatherTableViewCell, urlString: String) {
Task {
guard let url: URL = URL(string: urlString),
let (data, _) = try? await URLSession.shared.data(from: url),
let image: UIImage = UIImage(data: data) else {
return
}

setObjectImageChache(chache: imageChache, image: image, urlString: urlString)

setWeatherIconImage(table: tableView, index: index, cell: cell, image: image)

}
}

private func setObjectImageChache(chache: NSCache<NSString, UIImage>, image: UIImage, urlString: String) {
chache.setObject(image, forKey: urlString as NSString)
}

private func setWeatherIconImage(table: UITableView, index: IndexPath, cell: WeatherTableViewCell, image: UIImage) {
guard index == table.indexPath(for: cell) else { return }
cell.weatherIcon.image = image
}

}

18 changes: 12 additions & 6 deletions WeatherForecast/WeatherForecast/Weather.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,35 @@
import Foundation

// MARK: - Weather JSON Format
class WeatherJSON: Decodable {
struct WeatherJSON: Decodable {
let weatherForecast: [WeatherForecastInfo]
let city: City
}

// MARK: - List
class WeatherForecastInfo: Decodable {
struct WeatherForecastInfo: Decodable {
let dt: TimeInterval
let main: MainInfo
let weather: Weather
let dtTxt: String
}

// MARK: - MainClass
class MainInfo: Decodable {
struct MainInfo: Decodable {
let temp, feelsLike, tempMin, tempMax: Double
let pressure, seaLevel, grndLevel, humidity, pop: Double
}

// MARK: - Weather
class Weather: Decodable {
struct Weather: Decodable {
let id: Int
let main: String
let description: String
let icon: String
}

// MARK: - City
class City: Decodable {
struct City: Decodable {
let id: Int
let name: String
let coord: Coord
Expand All @@ -45,7 +45,7 @@ class City: Decodable {
}

// MARK: - Coord
class Coord: Decodable {
struct Coord: Decodable {
let lat, lon: Double
}

Expand All @@ -60,3 +60,9 @@ enum TempUnit: String {
}
}


// MARK: - ImageURLType
enum ImageURLType: String {
case path = "https://openweathermap.org/img/wn/"
case png = "@2x.png"
}
Comment on lines +65 to +68
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL String을 별도로 관리해보셨네요!👍

Loading