์๋ ํ์ธ์ Foma ์ ๋๋ค!!
์ ๋ฒ ์๊ฐ์ ํ์ฌ ์์น์ ๋ํ ๊ถํ์ ๋ฐ์์ค๊ณ ํ์ฌ ์์น๋ฅผ ์์๋ณด๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด์๋๋ฐ์.
ํน์๋ผ๋ ๋ชป๋ณด์ ๋ถ๋ค์ ์ฌ๊ธฐ ๋ก ์ด๋ํ์ ์ ๋ณด๊ณ ์์ฃผ์ธ์~
์ค๋์ ํ์ฌ ์์น์ ๋ ์จ์ ์์ผ๋ก์ 24์๊ฐ,์์ผ๋ก์ ์ผ์ฃผ์ผ๊ฐ ๋ ์จ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์์ ํ๋ฉด์ ๋์๋ณผ๊ฑฐ์์.
๋ฐ๋ก ์์ํ ๊ฒ์~
Preview
Weatherbit.io
๋ ์จ ์ ๋ณด๋ฅผ ๋ฐ๋ ์๋น์ค๋ ์๋์ ๊ฐ์ด weatherbit์์ ์ ๊ณตํ๋ API๋ฅผ ์ด์ฉํ์ต๋๋ค.
์ ์ฌ์ดํธ๋ก ์ด๋ํ์ ์ ํ์๊ฐ์ ํ ๋ก๊ทธ์ธ ํ๋ฉด ์๋์ ๊ฐ์ ํ๋ฉด์ด ๋ณด์ด์ค๊ฑฐ์์!
์ฌ๊ธฐ์ Key๋ฅผ ์ ์ ์ฅํด๋ก๋๋ค.
๊ทธ๋ค์ API Docs๋ฅผ ํด๋ฆญํ์๋ฉด ์๋์ ๊ฐ์ด ๋ฐ๊ฑด๋ฐ ํ๋์์ผ๋ก ํ์๋ API Documentation์ ๋๋ฌ์ค๋๋ค.
๊ทธ๋ฌ๋ฉด ์๋์ ๊ฐ์ด ์ฌ๋ฌ API Documentation์ด ๋ณด์ด์ค๊ฑด๋ฐ ์ฌ๊ธฐ์ ์ํ์๋ API๋ฅผ ์ฌ์ฉํ์๋ฉด ๋ฉ๋๋ค!
๊ทธ๋ฆฌ๊ณ ๋ ์จ์ ๋ฐ๋ฅธ ์์ด์ฝ์ด ํ์ํ๋ฐ ์๋ ์ฌ์ดํธ๋ก ์ ์ํ์ ์
์๋์ 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์ ์ด๋ค ํ๋ผ๋ฏธํฐ๋ค์ด ๋ด๊ฒจ์๋์ง ๋ณผ ์ ์์ต๋๋ค.
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()
}
}
}
}
์ ๋ค๋ณด๋ ๊ธ์ด ๋๋ฌด ๊ธธ์ด์ ธ์... ใ
๋ค์ ํฌ์คํ ์ ๋ ์จ ์ ๋ณด๋ฅผ ๋ฐ์์์ ๋์ฐ๋๋ฒ์ ์ ๋ฆฌํ๋๋ก ํ๊ฒ ์ต๋๋ค!!
๋ชจ๋ฅด์๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ธ์ ๋ ๋๊ธ ๋ฌ์์ฃผ์ธ์~~
๋๊ธ