Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
SwiftUI/SwiftUI(Intermediate)

17. JSON Download with @escaping

by Toughie 2023. 6. 12.

⭐️JSON Download with @escaping⭐️

지난 시간에 이어 인터넷 url을 통해 json 데이터를 받아와보자.

 

JSON 테스트 API

https://jsonplaceholder.typicode.com/

 

JSONPlaceholder - Free Fake REST API

{JSON} Placeholder Free fake API for testing and prototyping. Powered by JSON Server + LowDB. Tested with XV. Serving ~2 billion requests each month.

jsonplaceholder.typicode.com

api를 통해서 json을 받아올 때 활용할 수 있는 사이트.

이미지, 포스트 등 다양한 샘플이 존재함.

 

위 json의 구조를 보고 모델을 만들자.

만약 모델 만드는 것이 어렵다면(json이 복잡한 경우) 아래의 사이트도 이용할 수 있다.

https://app.quicktype.io/

 

Instantly parse JSON in any language | quicktype

 

app.quicktype.io

MODEL

struct PostModel: Identifiable, Codable {
    let userId: Int
    let id: Int
    let title: String
    let body: String
}

ViewModel

final class DownloadWithEscapingViewModel: ObservableObject {
    @Published var posts: [PostModel] = []
    init() {
        getPosts()
    }
    
    func getPosts() {
        //url을 받아온다.
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
        
        downloadData(fromURL: url) {
			...
        }
    }
	
	downloadData(fromURL: URL) { ... }
}

@escaping 클로저를 활용, url로 부터 json 데이터 다운로드

    func downloadData(fromURL url: URL, completionHandler: @escaping (_ data: Data?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            
            guard
                let data = data,
                error == nil,
                let response = response as? HTTPURLResponse,
                response.statusCode >= 200 && response.statusCode < 300 else {
                print("ERROR DONWLOADING DATA")
                completionHandler(nil)
                return
            }
            completionHandler(data)
        }.resume()
    }

URL에서 데이터를 다운로드하는 비동기적인 작업 수행.

completionHandler

- 데이터 다운로드 완료 후 호출되는 클로저(콜백함수) 다운로드 완료된 데이터(data)를 인자로 받음.

 

URLSession.shared.dataTask(with:completionHandler:)

- 기본적으로 백그라운드 스레드에서 실행됨.

- 데이터 다운로드 작업이 메인 스레드를 차단하지 않고 비동기적으로 실행

- 주어진 URL에 대한 데이터 테스크를 생성하고, 완료될 때 실행될 클로저를 지정

 

클로저 내부(data, response, error)

옵셔널, 여러 조건을 만족시키기 위해 guard 활용

데이터가 존재, 에러가 없어야함, response가 *HTTPURLResponse 타입이어야 하고,
statusCode가 200번대여야함.


*HTTPURLResponse

- HTTP 요청에 대한 서버의 응답을 나타내는 클래스

- HTTP 상태 코드, 헤더 필드, URL 등과 같은 응답 정보를 포함하고 있음

 

100번대(1XX)

- Informational responses

요청이 수신되었으며 처리 중임.

ex. 100 (Continue) - 서버가 요청을 수락하고 추가 정보를 기다리는 상태

⭐️200번대(2XX)

- Successful responses

요청이 성공적으로 처리되었음.

ex. 200 (OK) - 요청 성공, 요청된 리소스가 반환되었음.

300번대(3XX)

- Redirection messages

요청을 완료하기 위해 추가 동작이 필요함을 나타냄. 

ex. 301 (Moved Permanently) - 요청된 리소스의 위치가 영구적으로 변경되었음

400번대(4XX)

- Client error responses

클라이언트에 오류가 있음.

ex. 404(Not Found)는 요청된 리소스를 찾을 수 없음을 나타냄

500번대(5XX)

- Server error responses

서버에 오류가 발생함을 나타냄

ex. 500 (Internal Server Error) - 서버가 요청을 처리하는 동안 내부 오류가 발생했음을 나타냄


guard를 다 통과하고 나면 (데이터가 정상적으로 다운됐을 경우)

completionHandler에 data를 넘겨주며 호출함.

 

⭐️또한 데이터 작업을 시작하기 위해서

.resume() 메서드를 호출함.

 

Decoding

    func getPosts() {
        
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
        
        downloadData(fromURL: url) { data in
            if let data = data {
                guard let newPost = try? JSONDecoder().decode([PostModel].self, from: data) else { return }
                DispatchQueue.main.async { [weak self] in
//                    self?.posts.append(newPost)
                    self?.posts = newPost
                }
                print("데이터 다운로드 성공 & 포스트 추가 성공")
            } else {
                print("NO DATA RETURNED")
            }
        }

url을 통해 받아온 data를 completionHandler의 인자로 전달해주고 세부 구현

현재 json데이터의 형태가 Array이기 때문에, [PostModel].self 타입으로 디코딩

⭐️ @published 변수를 변경하는 작업으로 메인 스레드에서 진행해야함.

-> DispatchQueue.main.async { }로 메인스레드 이벤트 루프에 등록

또한 강한 순환참조를 방지하기 위해 weak self 캡쳐리스트 활용

메인스레드 비동기 처리를 해주지 않으면 보라색 경고를 만날 것이다 😈

정상적인 UI 업데이트를 위해서 메인 스레드에서 데이터 변경을 해줘야 한다.

-> Combine 쓰면서 좀 더 알아볼 예정!

'SwiftUI > SwiftUI(Intermediate)' 카테고리의 다른 글

19. Timer 타이머  (0) 2023.06.13
18. Combine 컴바인 .feat JSON  (0) 2023.06.13
16. Codable, JSON, Encodable, Decodable  (0) 2023.06.12
15. mask 마스킹  (0) 2023.05.26
14. sheet 시트 사용법 / multiple sheets  (0) 2023.05.25