์๋ ํ์ธ์ Foma ์ ๋๋ค!!
์ ๋ฒ ์๊ฐ์ ํ์ฌ ์์น์ ๋ํ ๊ถํ์ ๋ฐ์์ค๊ณ ํ์ฌ ์์น๋ฅผ ์์๋ณด๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด์๋๋ฐ์.
ํน์๋ผ๋ ๋ชป๋ณด์ ๋ถ๋ค์ ์ฌ๊ธฐ ๋ก ์ด๋ํ์ ์ ๋ณด๊ณ ์์ฃผ์ธ์~
์ค๋์ ํ์ฌ ์์น์ ๋ ์จ์ ์์ผ๋ก์ 24์๊ฐ,์์ผ๋ก์ ์ผ์ฃผ์ผ๊ฐ ๋ ์จ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ํ๋ฉด์ ๋์๋ณผ๊ฑฐ์์.
๋ฐ๋ก ์์ํ ๊ฒ์~
Preview
Weatherbit.io
๋ ์จ ์ ๋ณด๋ฅผ ๋ฐ๋ ์๋น์ค๋ ์๋์ ๊ฐ์ด weatherbit์์ ์ ๊ณตํ๋ API๋ฅผ ์ด์ฉํ์ต๋๋ค.
Weatherbit | Weather API - Historical Weather API
Simple. Powerful. Reliable. Getting access to weather data or historical weather data has never been easier. Unparalleled Forecast Accuracy We leverage the power of forecast models, and machine learning to deliver the most accurate weather forecast API on
www.weatherbit.io
์ ์ฌ์ดํธ๋ก ์ด๋ํ์ ์ ํ์๊ฐ์ ํ ๋ก๊ทธ์ธ ํ๋ฉด ์๋์ ๊ฐ์ ํ๋ฉด์ด ๋ณด์ด์ค๊ฑฐ์์!
์ฌ๊ธฐ์ Key๋ฅผ ์ ์ ์ฅํด๋ก๋๋ค.
๊ทธ๋ค์ API Docs๋ฅผ ํด๋ฆญํ์๋ฉด ์๋์ ๊ฐ์ด ๋ฐ๊ฑด๋ฐ ํ๋์์ผ๋ก ํ์๋ API Documentation์ ๋๋ฌ์ค๋๋ค.
๊ทธ๋ฌ๋ฉด ์๋์ ๊ฐ์ด ์ฌ๋ฌ API Documentation์ด ๋ณด์ด์ค๊ฑด๋ฐ ์ฌ๊ธฐ์ ์ํ์๋ API๋ฅผ ์ฌ์ฉํ์๋ฉด ๋ฉ๋๋ค!
๊ทธ๋ฆฌ๊ณ ๋ ์จ์ ๋ฐ๋ฅธ ์์ด์ฝ์ด ํ์ํ๋ฐ ์๋ ์ฌ์ดํธ๋ก ์ ์ํ์ ์
Weatherbit | Weather API metadata
www.weatherbit.io
์๋์ PNG Archive ํ์ผ์ ๋ค์ด๋ฐ์์ฃผ์ธ์!
Pod
์ ๊ทธ๋ผ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํด๋ณผ๊ฒ์!
Xcode ํ๋ก์ ํธ๋ฅผ ์์ฑํด์ฃผ์๊ณ podfile๋ถํฐ ๋ง๋ค์ด์ฃผ์ธ์!
๋ ์จ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๊ธฐ ์ํด Alamofire,SwiftyJSON ์ด๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผ ํ๊ธฐ๋๋ฌธ์
ํฐ๋ฏธ๋์์ ํด๋น ํ๋ก์ ํธ ์ด๋ ํ pod init์ ํด์ฃผ์๊ณ
์ฌ๋ฌ๋ถ๋ค๋ podfile์ ์ถ๊ฐํ์ ๋ค pod install ํด์ฃผ์ธ์~
pod 'SwiftyJSON', '~> 4.0'
pod 'Alamofire', '~> 5.2'
Assets
์์์ ๋ค์ด๋ฐ์ icons๋ฅผ
ํ๋ก์ ํธ์ Assets์ ๋ชจ๋ ๋ฃ์ด์ฃผ์ธ์!
KeyCenter
์์์ ์ ์ฅํ KEY๋ฅผ ์ด๋ ๊ฒ KeyCenter struct๋ฅผ ๋ง๋ค์ด์ ์ ์ฅํด์ค๋๋ค.
struct KeyCenter {
static let key = "YOUR_API_KEY"
}
LocationService
์ ๋ฒ ์๊ฐ์ ์ป์ด์จ ํ์ฌ ์์น ์๋์ ๊ฒฝ๋๋ฅผ LocationService ์ฑ๊ธํด์ผ๋ก ๋ง๋ค์ด์ฃผ์๊ณ ์ ์ฅํด์ฃผ์ธ์!
(์ฌ๊ธฐ ์์ ์ ๋ฒ ์๊ฐ์ ๋ฐฐ์ ๋ ์๋ ๊ฒฝ๋ ์ป์ด์ฌ๋ ๋ถ๋ถ์ ์ฐธ์กฐํด์ฃผ์ธ์!)
class LocationService {
static var shared = LocationService()
var longitude:Double!
var latitude:Double!
}
Layout
WeatherView.xib
xib ํ์ผ์ ๋ง๋ค์ด์ฃผ์๊ณ ์๋์ ๊ฐ์ด ๋ ์ด์์์ ๋ง๋ค์ด์ฃผ์ธ์!
์ ๋ถ๋ถ์ ํ์ฌ ๋ ์จ๋ฅผ ๋์ธ ๋ทฐ์ ๋๋ค.
๊ฐ cityLabel,dateLabel,tempLabel๋ก ๊ตฌ์ฑํด์ฃผ์ธ์.
์๋๋ hourlyCollectionView๋ก 24์๊ฐ์ ๋ ์จ ์ ๋ณด๋ฅผ ์ปฌ๋ ์ ๋ทฐ์ ๋๋ค.
์ปฌ๋ ์ ๋ทฐ ์๋ ํ ์ด๋ธ๋ทฐ๋ weeklyTableView๋ก 2์ฃผ๊ฐ์ ๋ ์จ ์ ๋ณด๋ฅผ ๋ฐ์์ฌ ํ ์ด๋ธ๋ทฐ์ ๋๋ค.
WeatherView
WeatherView ํ์ผ์ ๋ง๋ค์ด์ฃผ์ ํ ์์๋ ์ ๋ชจ๋ ์ฐ๊ฒฐํด์ฃผ์ธ์!
class WeatherView: UIView {
//MARK:IBOutlets
@IBOutlet weak var cityLabel: UILabel!
@IBOutlet weak var dateLabel: UILabel!
@IBOutlet weak var tempLabel: UILabel!
@IBOutlet weak var weatherInfoLabel: UILabel!
@IBOutlet weak var weeklyTableView: UITableView!
@IBOutlet weak var hourlyCollectionView: UICollectionView!
HourlyCollectionViewCell.xib
์๋์ ๊ฐ์ด HourlyCollectionViewCell์ xib๋ก ๋ง๋ค์ด์ฃผ์๊ณ ์ Label์ timeLabel๋ก
๊ฐ์ด๋ฐ ์ด๋ฏธ์ง๋ weatherImage , ์๋๋ timeLabel๋ก ๋ ์ด์์ํด์ฃผ์๊ณ
HourlyCollectionViewCell
HourlyCollectionViewCell์ ๋ชจ๋ ์ฐ๊ฒฐํด์ค๋๋ค.
class HourlyCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var tempLabel: UILabel!
@IBOutlet weak var weatherImage: UIImageView!
@IBOutlet weak var timeLabel: UILabel!
WeeklyTableViewCell.xib
์ผ์ชฝ Lable์ ๋ ์ง๋ฅผ ํ์ํ weekLabel๋ก
๊ฐ์ด๋ฐ๋ ๋ ์จ ์์ด์ฝ์ ํ์ํ weatherImage๋ก
์ค๋ฅธ์ชฝ ๋ ์ด๋ธ์ ์จ๋๋ฅผ ํ์ํ tempLabel๋ก ๋ง๋ค์ด์ค๋๋ค.
WeeklyTableViewCell
๋ง์ฐฌ๊ฐ์ง๋ก ๋ชจ๋ ์ฐ๊ฒฐํด์ค๋๋ค.
class WeeklyTableViewCell: UITableViewCell {
@IBOutlet weak var weekLabel: UILabel!
@IBOutlet weak var weatherImage: UIImageView!
@IBOutlet weak var tempLabel: UILabel!
CurrentWeather
์ด๋ ๊ฒ ๋ ์ด์์์ ๋๋ด์ จ๋ค๋ฉด ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ๋ ์จ ์ ๋ณด๋ฅผ ๋ฐ์์ค๋ ์์ ์ ํด๋ณผ๊ฒ์!
ํ์ฌ ์์น์ ๋ ์จ
CurrentWeatehr ํด๋์ค๋ฅผ ํ๋ ๋ง๋ค์ด์ค๋๋ค.
์ ์ผ ๋จผ์ Alamofire์ SwiftyJSON์ import ํด์ฃผ์ธ์!
import Alamofire
import SwiftyJSON
๊ทธ ๋ค์์ผ๋ก ๋์์ ๋ ์ง ์จ๋ ๋ ์จ์ ๋ณด๋ฅผ ๋ฐ์์ฌ ๋ณ์๋ค์ ๋ง๋ค์ด์ค๋๋ค.
var city:String = ""
var date:String = ""
var temp:Double = 0.0
var weatherInfo:String = ""
์๋์ ๊ฐ์ด ํ์ฌ ๋ ์จ๋ฅผ ๋ฐ์์ฌ ํจ์๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
func getCurrentWeather(completion:@escaping() -> Void) {
let lon = LocationService.shared.longitude!
let lat = LocationService.shared.latitude!
let path = "https://api.weatherbit.io/v2.0/current?lat=\(lat)&lon=\( lon)&key=\(KeyCenter.key)&include=minutely"
AF.request(path).responseJSON { (response) in
let result = response.result
switch result {
case .success(let value as [String:Any]):
let json = JSON(value)
let data = json["data"][0]
self.city = data["city_name"].stringValue
self.date = currentDateFromUnix(unixDate: data["ts"].double)?.shortDate() ?? ""
self.temp = data["temp"].double ?? 0.0
self.weatherInfo = data["weather"]["description"].stringValue
completion()
case .failure(let error):
print(error.errorDescription ?? "")
default:
fatalError()
}
}
}
๊ฐ๋จํ๊ฒ ์ค๋ช ๋๋ฆฌ๋ฉด
์์์ lon,lat ๋ณ์๋ LocationService์ ์ ์ฅํด๋์๋ ์๋์ ๊ฒฝ๋์ ๋๋ค.
let lon = LocationService.shared.longitude!
let lat = LocationService.shared.latitude!
๊ทธ๋ฆฌ๊ณ path๋ฅผ ์๋์ ๊ฒฝ๋๋ฅผ ๋ฃ์ด์ฃผ์๊ณ KeyCenter์ ์ ์ฅํด๋์๋ APIํค๋ฅผ ์ด์ฉํด์ ํ์ฌ ๋ ์จ ์ ๋ณด๊ฐ ๋ด๊ธด url ๊ฒฝ๋ก๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
let path = "https://api.weatherbit.io/v2.0/current?lat=\(lat)&lon=\( lon)&key=\(KeyCenter.key)&include=minutely"
๊ทธ ๋ค์์ผ๋ก Alamofire ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด path๋ฅผ ํตํด ๋ฐ์ JSON ๋ฐ์ดํฐ์ ์๋ต์ ๋ฐ์์์ค๋๋ค.
AF.request(path).responseJSON { (response) in... }
์๋ต์ ๋ํ ๊ฒฐ๊ณผ๋ฅผ ๋ง๋ค์ด์ฃผ์๊ณ
let result = response.result
swiftch case ๊ตฌ๋ฌธ์ ํ์ฉํด ๊ฐ ์๋ต์ ๋ํ ๊ฒฐ๊ณผ๋ฅผ ์๋์ ๊ฐ์ด ๋ง๋ค์ด์ค๋๋ค.
๋ง์ฝ ์๋ต์ด ์ฑ๊ณต์ ์ผ๋ก ๋ฐ์์์ก์ ๊ฒฝ์ฐ
json์ผ๋ก ๋ฐ์์จ ๋ฐ์ดํฐ๋ฅผ [String:Any] ๋์ ๋๋ฆฌ๋ก ๋ง๋ค์ด์ฃผ๊ธฐ ์ํด์ SwiftyJSON ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด
JSON(value)๋ก ๋ง๋ค์ด์ค ๋ค์ ๊ฐ ํ๋ผ๋ฏธํฐ๋ค์ ์ ๋ณด๋ฅผ ๊ฐ ์์น์ ๋ง๊ฒ ๋ฃ์ด์ค๋๋ค.
switch result {
case .success(let value as [String:Any]):
let json = JSON(value)
let data = json["data"][0]
self.city = data["city_name"].stringValue
self.date = currentDateFromUnix(unixDate: data["ts"].double)?.shortDate() ?? ""
self.temp = data["temp"].double ?? 0.0
self.weatherInfo = data["weather"]["description"].stringValue
completion()
case .failure(let error):
print(error.errorDescription ?? "")
default:
fatalError()
}
JSON ๋ฐ์ดํฐ ์ ๋ณด
์๋ ์ฌ์ดํธ๋ก ์ด๋ํ์๋ฉด json data์ ์ด๋ค ํ๋ผ๋ฏธํฐ๋ค์ด ๋ด๊ฒจ์๋์ง ๋ณผ ์ ์์ต๋๋ค.
Weatherbit | Current Weather API Documentation
Current Weather API This API returns current conditions from our network of over 47,000 sub-hourly reporting weather stations. Every API request will return the nearest, and most recent observation. All parameters should be supplied to the Weather API as q
www.weatherbit.io
Helpers & Extensions
์์์ currentDateFromUnix์ shortDate() ๋ถ๋ถ์์ ์ค๋ฅ๊ฐ ์๊ธธ๊ฑฐ์์.
์ด ๋ ๋นํฉํ์ง ๋ง์๊ณ ์๋ก์ด Swift ํ์ผ์ ๋ง๋ค์ด์ฃผ์๊ณ ์๋์ ๊ฐ์ด ๋ถ์ฌ๋ฃ์ด์ฃผ์ธ์
์๊ฐ๊ณผ ๋ ์ง๋ฅผ ๋ณํํด์ฃผ๋ ๋ฉ์๋์ ๋๋ค.
import UIKit
//Double๋ก ์ค๋ ํ์์คํฌํ ๋ฐ์ดํธ ํ์์ผ๋ก ๋ณํ
func currentDateFromUnix(unixDate:Double?) -> Date? {
if unixDate != nil {
return Date(timeIntervalSince1970: unixDate!)
}else{
return Date()
}
}
//๋ ์จ ์์ด์ฝ ์ด๋ฆ์ ๋ฐ๋ผ์ ์ด๋ฏธ์ง๋ก ๋ณํ
func getWeatherIconFor(_ type:String) -> UIImage? {
return UIImage(named: type)
}
import Foundation
extension Date {
func shortDate() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM d"
return dateFormatter.string(from: self)
}
func time() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm"
return dateFormatter.string(from: self)
}
}
HourlyWeather
24์๊ฐ ๋ ์จ ์ ๋ณด๋ฅผ ๋ฐ์์ฌ ํด๋์ค๋ ๋ง๋ค์ด์ค๋๋ค.
(์์ธํ ์ค๋ช ์ ์ CurrentWeather์ ์ฐธ๊ณ ํด์ฃผ์ธ์.)
import Alamofire
import SwiftyJSON
class HourlyWeather {
var date:String = ""
var temp:Double = 0.0
var weatherIcon:String = ""
init(weatherDictionary:Dictionary<String,Any>) {
let data = JSON(weatherDictionary)
self.date = currentDateFromUnix(unixDate: data["ts"].double)?.time() ?? ""
self.temp = data["temp"].double ?? 0.0
self.weatherIcon = data["weather"]["icon"].stringValue
}
class func getHourlyWeather(completion:@escaping([HourlyWeather]) -> Void) {
let lon = LocationService.shared.longitude!
let lat = LocationService.shared.latitude!
let path = "https://api.weatherbit.io/v2.0/forecast/hourly?lat=\(lat)&lon=\(lon)&key=\(KeyCenter.key)"
AF.request(path).responseJSON { (response) in
let result = response.result
var hourlyWeathers = [HourlyWeather]()
switch result {
case .success(let value as [String:Any]):
if let data = value["data"] as? [Dictionary<String,AnyObject>] {
data.forEach{hourlyWeathers.append(HourlyWeather(weatherDictionary: $0))}
}
completion(hourlyWeathers)
case .failure(let error):
print(error.errorDescription ?? "")
default:
fatalError()
}
}
}
}
WeeklyWeather
2์ฃผ๊ฐ์ ๋ ์จ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ฌ Weeklyweahter ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ์๋์ ๊ฐ์ด ๋ถ์ฌ๋ฃ์ด์ฃผ์ธ์!
(์์ธํ ์ค๋ช ์ ์ CurrentWeather์ ์ฐธ๊ณ ํด์ฃผ์ธ์.)
import Alamofire
import SwiftyJSON
class WeeklyWeather {
var date:String = ""
var temp:Double = 0.0
var weatherIcon:String = ""
init(weatherDictionary:Dictionary<String,Any>) {
let data = JSON(weatherDictionary)
self.date = String("\(currentDateFromUnix(unixDate: data["ts"].double) ?? Date())".prefix(10))
self.temp = data["temp"].double ?? 0.0
self.weatherIcon = data["weather"]["icon"].stringValue
}
class func getWeeklyWeather(completion:@escaping ([WeeklyWeather]) -> Void) {
let lat = LocationService.shared.latitude!
let lon = LocationService.shared.longitude!
let path = "https://api.weatherbit.io/v2.0/forecast/daily=7?lat=\(lat)&lon=\(lon)&key=\(KeyCenter.key)"
AF.request(path).responseJSON { (response) in
let result = response.result
var weeklyWeathers = [WeeklyWeather]()
switch result {
case .success(let value as [String:Any]) :
if let data = value["data"] as? [Dictionary<String,AnyObject>] {
data.forEach {weeklyWeathers.append(WeeklyWeather(weatherDictionary: $0))}
}
completion(weeklyWeathers)
case .failure(let error):
print(error.errorDescription ?? "")
default :
fatalError()
}
}
}
}
์ ๋ค๋ณด๋ ๊ธ์ด ๋๋ฌด ๊ธธ์ด์ ธ์... ใ
๋ค์ ํฌ์คํ ์ ๋ ์จ ์ ๋ณด๋ฅผ ๋ฐ์์์ ๋์ฐ๋๋ฒ์ ์ ๋ฆฌํ๋๋ก ํ๊ฒ ์ต๋๋ค!!
๋ชจ๋ฅด์๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ธ์ ๋ ๋๊ธ ๋ฌ์์ฃผ์ธ์~~
๋๊ธ