티스토리 뷰
오늘은 지난 번에 요청한 데이터를 URLSession을 통해 원하는 형식으로 바꿔보는 작업을 해볼게요!
가장 먼저는 각 레이블들의 레이아웃을 잡아줄게요!
이 부분은 간단하므로 더보기로 보시면 돼요!
class ViewController: UIViewController {
let tempLbl = UILabel()
let feelsLikeLbl = UILabel()
let idLbl = UILabel()
let mainLbl = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
getWeather()
}
}
//MARK: -UI
extension ViewController {
final private func configureUI() {
setAttributes()
setConstraints()
}
final private func setAttributes() {
tempLbl.text = "temp"
feelsLikeLbl.text = "feels_Like"
idLbl.text = "id"
mainLbl.text = "main"
}
final private func setConstraints() {
[tempLbl, feelsLikeLbl, idLbl, mainLbl].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
tempLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
tempLbl.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
feelsLikeLbl.topAnchor.constraint(equalTo: tempLbl.bottomAnchor, constant: 15),
feelsLikeLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
idLbl.topAnchor.constraint(equalTo: feelsLikeLbl.bottomAnchor, constant: 15),
idLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
mainLbl.topAnchor.constraint(equalTo: idLbl.bottomAnchor, constant: 15),
mainLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
])
}
}
이렇게 잡으면 아래처럼 제가 받아오고 싶은 각 정보들을 화면에 나타내는 기본적인 작업이 끝납니다!

받아온 데이터를 받아와서 사용하기 위해서는 4가지만 기억하면 됩니다요.
1. URL 링크 만들기 (1편 참고)
2. URLSession 만들기
3. URLSession에 task 주기
4. task 시작하기
ViewController를 확장하여 getWeather라는 함수에서 작업을 진행해렵니다.
extension ViewController {
private func getWeather() {
// 1. URL 구조체 만들기 (1편 참고)
// 2. URLSession 만들기
// 3. URLSession에 task 주기
// 4. task 시작하기
}
}
1. URL 구조체 만들기 (1편 참고)
url은 아래와 같아요. 마지막 파라미터인 appid={API key}에는 본인의 키를 넣으면 돼요!
extension ViewController {
private func getWeather() {
// 1. URL 구조체 만들기 (1편 참고)
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?appid={API키}&q=seoul&units=metric") else { return }
// 2. URLSession 만들기
// 3. URLSession에 task 주기
// 4. task 시작하기
}
}
해당 url을 가지고 URL 구조체를 만들어줘요. 호옥시 url이 nil일수도 있으니, guard let을 통해 옵셔널 바인딩을 해줍니당.
2. URLSession 만들기
URLSession은 swift에서 서버와 네트워킹을 위해 만들어 놓은 클래스에요.
Apple Developer Documentation
developer.apple.com
URLSession은 싱글톤 객체를 가지고 있는데요. 이 객체를 이용해볼게요.
아니면 init(configuration: URLSessionConfiguration) 이라는 생성자를 이용할 수도 있어요.
let urlSession = URLSession.init(configuration: .default)
extension ViewController {
private func getWeather() {
// 1. URL 구조체 만들기 (1편 참고)
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?appid=4999a8c6b45fbd5bb0de1b72f2690fef&q=seoul&units=metric") else { return }
// 2. URLSession 만들기
let urlSession = URLSession.shared
// 3. URLSession에 task 주기
// 4. task 시작하기
}
}
3. URLSession에 task 주기
task라는 상수를 만들어 dataTask를 할당해볼게요.
이 메서드는 URL을 파라미터로 받은 후 요청이 완료되면 completionHandler에서 그 결과(data, response, error)를 처리합니다.
우리가 요청한 바에 따라 결과가 오게 되는데요. 결과는 data가 될수도 있고, error가 될 수도 있고, 응답(response)이 될수도 있어요.
extension ViewController {
private func getWeather() {
// 1. URL 구조체 만들기 (1편 참고)
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?appid={API키}f&q=seoul&units=metric") else { return }
// 2. URLSession 만들기
let urlSession = URLSession.shared
// 3. URLSession에 task 주기
let task = urlSession.dataTask(with: url) { data, response, error in
// data, response, error에 대한 처리해주기
}
// 4. task 시작하기
}
}
(1) error인 경우
우선 응답이 error일 때부터 처리해줄게여. ERROR? 타입이기 때문에 guard let을 통해 에러인 경우, early exit 해줍니다.
guard error == nil else { return print(error!) }
(2) response인 경우
HTTP의 응답코드는 1XX, 2XX, 3XX, 4XX, 5XX와 같이 5개의 클래스로 분류할 수 있어요.
그 중에서 200..<400에 해당하는 응답만 성공(또는 redirection)이기 때문에 또 guard let을 통해 옵셔널 바인딩 해줍니다.
guard let response = response as? HTTPURLResponse, (200..<400).contains(response.statusCode) else { return print("Invalid Response") }
(3) data인 경우
DATA?타입이 때문에 data도 guard let 바인딩을 해주고요.
guard let data = data else { return }
그리고! 이제 JSONDecode라는 클래스를 통해 데이터를 해독해줍니다요.
우선 JSONDecoder의 인스턴스를 찍어주고, decode라는 메서드를 통해 해독해줄게요.
근데 이 decode는 에러를 throwing하는 메서드군요.
그러면 do-catch문으로 해결을 해주야 해요.
do-catch 문은 decode처럼 에러를 throws하는 메서드를 처리할 때 사용하는 에러처리 문법이에요.
보통 메서드를 실행할 때는 '함수이름( )'로 실행을 하지만, throws 메서드의 경우에는 do { } 에 넣어서 실행을 해야 해요. try 키워드와 함께 말이져.
아래에서도 decode 메서드를 do { } 안에 넣고, try 키워드를 넣어서 처리해줬어여.
그리고 에러를 던지는 경우에는 catch 구문에서 처리해주면 됩니다요.
do {
let jsonDecoder = JSONDecoder()
let decodeData = try jsonDecoder.decode(WeatherData.self, from: data)
} catch {
print("Parshing Error") print(error)
}
decode의 파라미터는 2개인데요, type은 JSON 객체로부터 디코딩할 값의 타입, data는 디코딩할 JSON 객체입니다.
이번에는 type파라미터에는 2편에서 만든 WeatherData를 넣구요, from 파라미터에는 completionHandler에서 바인딩했던 data를 넣어줘요.
드디어 2편에서 만들어줬던 WeatherData가 디코딩되어 decodeData에 담겼습니다.
벌써 아득하지만, temp, feelsLike, id, main만 화면에 나타낼거였죠?
각각에 대한 상수를 만듭니다.
do {
let jsonDecoder = JSONDecoder()
let decodeData = try jsonDecoder.decode(WeatherData.self, from: data)
let temp = decodeData.main.temp
let feelsLike = decodeData.main.feelsLike
let id = decodeData.weather[0].id
let main = decodeData.weather[0].main
} catch {
print("Parshing Error")
print(error)
}
이 때 주의할 점!!
WeatherData 구조체를 만들어줬을 때 weather의 타입을 [weather]로 만들어줬던 거 기억나시져?
(기억이 안 나면 여기)
import Foundation
struct WeatherData: Decodable {
let main: Main
let weather: [Weather]
struct Main: Decodable {
let temp, feelsLike, tempMin, tempMax: Double
let pressure, humidity: Int
enum CodingKeys: String, CodingKey {
case temp
case feelsLike = "feels_like"
case tempMin = "temp_min"
case tempMax = "temp_max"
case pressure, humidity
}
}
struct Weather: Decodable {
let id: Int
let main, weatherDescription, icon: String
enum CodingKeys: String, CodingKey {
case id, main
case weatherDescription = "description"
case icon
}
}
}
그렇기 때문에 id랑 main에 접근할 때는 배열 문법(대괄호)을 사용해줘요.
아래처럼 말이져.
let id = decodeData.weather[0].id
let main = decodeData.weather[0].main
그리고 마지막! 메인쓰레드로 보내주는 작업이 남았어요.
URLSession은 기본적으로 비동기적으로 처리가 돼요. 네트워킹 작업이기 때문에 메인쓰레드에서 작업하는 게 오히려 비효율적이겠죠?
근데 우리가 레이블에 받아온 정보를 나타내는 일 (UI관련된 일)은 메인쓰레드에서 처리가 되어야 된단 말이져?
그래서! 메인쓰레드로 보내주는 작업이 하나 더 필요해요.
DispatchQueue.main.async {
self.tempLbl.text = "temp: \(String(temp))"
self.tempLbl.backgroundColor = .lightGray
self.feelsLikeLbl.text = "feels_like: \(String(feelsLike))"
self.feelsLikeLbl.backgroundColor = .lightGray
self.idLbl.text = "id: \(String(id))"
self.idLbl.backgroundColor = .lightGray
self.mainLbl.text = "main: \(String(main))"
self.mainLbl.backgroundColor = .lightGray
}
4. task 시작하기
이제 마지막으로, task을 시작해주면 끝!
task.resume()
왕기초기초기초임에도 불구하고 매우 어려웠지만, 그래도 일단은 화면에는 떴네요!
혹시라도 잘못된 부분이 있다면, 언제든지 알려주십셔 슨배님덜!
많이 배우겠습니다!
전체코드
//
// ViewController.swift
// URLSession-Starter
//
// Created by 순진이 on 2021/12/27.
//
import UIKit
class ViewController: UIViewController {
let tempLbl = UILabel()
let feelsLikeLbl = UILabel()
let idLbl = UILabel()
let mainLbl = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
getWeather()
}
}
extension ViewController {
private func getWeather() {
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?appid=4999a8c6b45fbd5bb0de1b72f2690fef&q=seoul&units=metric") else { return }
let urlSession = URLSession.shared
let task = urlSession.dataTask(with: url) { data, response, error in
//에러 여부 체크
guard error == nil else { return print(error!) }
//응답 코드 확인
guard let response = response as? HTTPURLResponse,
(200..<400).contains(response.statusCode) else { return print("Invalid Response") }
//데이터 확인
guard let data = data else { return }
do {
let jsonDecoder = JSONDecoder()
let decodeData = try jsonDecoder.decode(WeatherData.self, from: data)
let temp = decodeData.main.temp
let feelsLike = decodeData.main.feelsLike
let id = decodeData.weather[0].id
let main = decodeData.weather[0].main
DispatchQueue.main.async {
self.tempLbl.text = "temp: \(String(temp))"
self.tempLbl.backgroundColor = .lightGray
self.feelsLikeLbl.text = "feels_like: \(String(feelsLike))"
self.feelsLikeLbl.backgroundColor = .lightGray
self.idLbl.text = "id: \(String(id))"
self.idLbl.backgroundColor = .lightGray
self.mainLbl.text = "main: \(String(main))"
self.mainLbl.backgroundColor = .lightGray
}
} catch {
print("Parshing Error")
print(error)
}
}
task.resume()
// 에러 : 있을수도 있고, 없을수도 있기 때문에 옵셔널 형태
// 데이터 : 에러가 없을 때 데이터가 있음
// 레스폰즈 : 결과
}
}
//MARK: -UI
extension ViewController {
final private func configureUI() {
setAttributes()
setConstraints()
}
final private func setAttributes() {
tempLbl.text = "temp"
feelsLikeLbl.text = "feels_Like"
idLbl.text = "id"
mainLbl.text = "main"
}
final private func setConstraints() {
[tempLbl, feelsLikeLbl, idLbl, mainLbl].forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
tempLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
tempLbl.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
feelsLikeLbl.topAnchor.constraint(equalTo: tempLbl.bottomAnchor, constant: 15),
feelsLikeLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
idLbl.topAnchor.constraint(equalTo: feelsLikeLbl.bottomAnchor, constant: 15),
idLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
mainLbl.topAnchor.constraint(equalTo: idLbl.bottomAnchor, constant: 15),
mainLbl.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
])
}
}
//
// WeatherData.swift
// URLSession-Starter
//
// Created by 순진이 on 2022/01/10.
//
import Foundation
struct WeatherData: Decodable {
let main: Main
let weather: [Weather]
struct Main: Decodable {
let temp, feelsLike, tempMin, tempMax: Double
let pressure, humidity: Int
enum CodingKeys: String, CodingKey {
case temp
case feelsLike = "feels_like"
case tempMin = "temp_min"
case tempMax = "temp_max"
case pressure, humidity
}
}
struct Weather: Decodable {
let id: Int
let main, weatherDescription, icon: String
enum CodingKeys: String, CodingKey {
case id, main
case weatherDescription = "description"
case icon
}
}
}
'iOS 개발자 되기' 카테고리의 다른 글
[swift] 왕기초적인 API 받아오기 2 (JSON Parsing) (1) | 2022.01.16 |
---|---|
[swift] 왕기초적인 API 받아오기 1 (0) | 2022.01.16 |
[swift] stepper 및 alertController 연습하기 3-2 (in Code) (0) | 2021.12.04 |
[swift] stepper 및 alertController 연습하기 3-1 (in Code) (0) | 2021.12.01 |
[swift] stepper 및 alertController 연습하기 2 (in Code) (0) | 2021.11.30 |
- Total
- Today
- Yesterday