티스토리 뷰

URLSession

- ViewController


import UIKit

class ViewController: UIViewController {
   
    let temperatureLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
        NetworkService.shared.fetch(urlString: URL.weather) { weatherInfo in
            self.temperatureLabel.text = String(weatherInfo.main.temp)
        }
    }
}

//MARK: -UI
extension ViewController {
    final private func configureUI() {
        setAttributes()
        setConstraints()
    }
    
    final private func setAttributes() {
        temperatureLabel.text = "0"
    }

    final private func setConstraints() {
        view.addSubview(temperatureLabel)
        temperatureLabel.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            temperatureLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            temperatureLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
    }
}

- Model

import Foundation

struct WeatherInfo: Codable {
    let main: Main
    let weather: [Weather]
    
    struct Main: Codable {
        let temp: Double
    }
    
    struct Weather: Codable {
        let main: String
    }
}

- Service

import Foundation

// singleton으로 사용
class NetworkService {

    static let shared = NetworkService()

    func fetch(urlString: String, completion: @escaping (WeatherInfo)-> Void) {
        URLSession.shared.dataTask(with: URL(string: urlString)!) { data, response, error in
            // 1. error가 있다면
            guard error == nil else { fatalError() }
            
            // 2. response가 있으면서 응답 코드가 200 ~ 400 사이일 때 (응답이 성공했을 때)
            guard let response = response as? HTTPURLResponse, (200..<400).contains(response.statusCode) else { fatalError() }

            // 3. data가 있다면
            guard let data = data else { return }
            
            do {
                let decodedData = try JSONDecoder().decode(WeatherInfo.self, from: data)
                
                DispatchQueue.main.async {
                    // UI에 대한 작업이기 때문에 main thread에서 해줘야 함.
                    // main에서는 항상 async만 써야 함 (sync X)
                    print(Thread.isMainThread) // true
                    completion(decodedData)
                }
                
            } catch {
                fatalError()
            }
        }.resume()
    }
    
    private init() {}
}
extension URL {
    static let weather = "https://api.openweathermap.org/data/2.5/weather?q=seoul&appid"
}

 

1. 응답(response)는 HTTPURLResponse로 타입캐스팅 해줘야 함

guard let response = response as? HTTPURLResponse else { fatalError() }

 

2. 응답(response)이 성공일 경우에만 data 받아옴

guard let (200..<400).contains(response.statusCode) else { fatalError() }

 

3. 해당 thread가 메인 thread인지 확인하는 방법

print(Thread.isMainThread)

 

4. URLSession은 비동기적으로 일어나기 때문에 @escaping처리가 필요

func fetch(urlString: String, completion: @escaping (WeatherInfo)-> Void) { }
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크