⭐️JSON Download with @escaping⭐️
지난 시간에 이어 인터넷 url을 통해 json 데이터를 받아와보자.
JSON 테스트 API
https://jsonplaceholder.typicode.com/
api를 통해서 json을 받아올 때 활용할 수 있는 사이트.
이미지, 포스트 등 다양한 샘플이 존재함.
위 json의 구조를 보고 모델을 만들자.
만약 모델 만드는 것이 어렵다면(json이 복잡한 경우) 아래의 사이트도 이용할 수 있다.
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 |