진행 방식은 간략하게 다음과 같음.
1. session configuration을 지정하고 session을 만들기
- session configuration을 따로 만들지 않고 shared session을 사용할 수도 있슴
2. URLRequest 만들기
3. session 내에 task 추가
4. task 실행
지금은 이 흐름이 이해가 안 될 수도 있는데, 하나하나 개념을 알고 나면 충분히 이해될 것임! URLSessionConfiguration 먼저 차근차근 알아가봅시다.
→ 한마디로 일단 서버와 데이터를 주고받으려면 configuration을 만들어야 한다는 것. 근데 난 한번도 configuration을 생성한 적이 없고… 항상 shared를 썼던 것 같은데… 하는 사람들도 많을 것임. 내가 그랬기 때문에 💦
그렇다면 shared session을 포함한 session의 종류들부터 알아보자!
shared 클래스의 메소드를 호출하여 접근 가능shared에 접근하기만 하면됨.
하지만 shared session만 사용하지 않고, configuration 객체가 존재하는 이유가 있다. shared session에는 몇 가지 제한 사항이 있다고 함.
URLSessionDataDelegate 의 urlSession(_:dataTask:didReceive:)서버로부터 데이터가 도착하면 데이터를 점진적으로 얻을 수 없음
아까 shared session에서는 이런 제약 사항이 있다고 했는데, delegate 방식으로 사용하면 task가 실행되는 동안에도 세부적인 처리를 해줄 수 있음
우리는 session 내에서 데이터를 주고 받는 task를 생성할 수 있다.
NSData 객체를 통해 데이터를 보내고 받음
GET, POST 등)이 포함됨httpMethod 프로퍼티의 기본값은 GET나는 현재 우주에 있는 사람 수랑 그 사람들에 대한 정보를 리스트로 보여주는 재밌는 open api가 있길래 요걸 사용했다.
How Many People Are In Space Right Now
서버 통신에 관한 기본적인 내용은 일단은 패스하고!
간단하게 받아오는 데이터 중에서 지금 우주에 몇 명이 있는지만 뷰에 나타내 보려고 한다.
예시에 대한 정보는 대충 요정도일 것 같다.
따라서 그냥 URLSession을 어떻게 사용하는지에만 집중하면 될 듯함!
먼저 data task를 추가하는 코드로 구현하는 방법만 간략하게 보고 가려고 한다.

with 파라미터도 URLRequest와 URL 타입으로 받을 수 있음
내가 통신할 api의 http method는 get 으로 기본값이고, 따로 정의할 게 없어서 그냥 URL 타입으로 넣어줄 예정
completionHandler 클로저는 서버로부터 데이터가 왔을 때 실행됨
따라서, 받아온 데이터를 어딘가에 저장하거나 보여주려면 completionHandler을 써야 한다.
앞서 말했던 것처럼 completionHandler가 아니라 delegate data task를 추가할 수도 있음!
앞부분을 다 이해했으면 알겠지만 나는 shared session을 사용할 것이므로 delegate는 사용할 수 없다.
그럼 예제 ㄱㄱ
1. reponse에 맞게 모델을 만들기
{
"message": "success",
"number": NUMBER_OF_PEOPLE_IN_SPACE,
"people": [
{"name": NAME, "craft": SPACECRAFT_NAME},
...
]
}
struct SpaceResponse: Codable {
let message: String
let people: [Person]
let number: Int
}
struct Person: Codable {
let name, craft: String
}
2. session을 만들고 task 추가하기
class DataManager {
func getPeopleNumber(handler: @escaping (_ number: Int?, _ error: Error?) -> ()) {
let url = "http://api.open-notify.org/astros.json"
URLSession.shared.dataTask(with: URL(string: url)!) { data, response, error in
guard let data = data,
let response = response as? HTTPURLResponse,
response.statusCode == 200 else { return }
do {
let decodedResponse = try JSONDecoder().decode(SpaceResponse.self, from: data)
handler(decodedResponse.number, error)
} catch {
print("디코딩 오류 🚨")
}
}
.resume()
}
}
func dataTask(
with url: URL,
completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void
) -> URLSessionDataTask
completionHandler 는 요청이 완료됐을 때 실행됨. completionHandler 의 파라미터를 살펴보자.
Data? 타입Data 타입은 아까 만들어놓은 response 모델로 디코딩해야 사용할 수 있음
JSONDecoder의 decode 메서드를 통해 Data 타입 → 제작한 response 모델로 디코딩 가능
URLResponse? 타입HTTPURLResponse 객체로 응답이 온다
HTTPURLResponse 는 URLResponse를 상속하기 때문에 다운캐스팅 필요요청에 성공하면 nil이, request가 실패했을 때는 Error가 나타남
suspended(중단된) 상태의 task를 resume(다시 시작)시키는 메소드
resume 을 호출해 주어야 하는 이유는, 새로 만들어진 task들은 중단된 상태로 시작하기 때문임! 따라서 우리가 시작하도록 만들어줘야 함.
3. viewModel과 view에서 메서드 호출
class SpaceNumberViewModel: ObservableObject {
@Published var peopleNumber: Int?
let dataManager = DataManager()
func fetchPeopleNumber() {
dataManager.getPeopleNumber { [weak self] number, error in
DispatchQueue.main.async {
self?.peopleNumber = number
}
}
}
}
viewModel에서 dataManager의 getPeopleNumber을 호출하고,
디코딩해서 넘겨받은 number을 viewModel의 Published 프로퍼티에 할당해 줌
SwiftUI가 익숙하지 않은 사람들을 위해 잠깐 쓰자면! @Published 프로퍼티가 있는 객체가 변경될 때마다 해당 객체를 사용하고 있는 뷰들은 다시 로드돼서 반영을 하게 됨.
struct SpaceNumberView: View {
@StateObject private var viewModel = SpaceNumberViewModel()
var body: some View {
VStack {
Text("우주에는 지금 몇 명이 있을까?")
Text("\(viewModel.peopleNumber ?? 0)")
}
.onAppear {
viewModel.fetchPeopleNumber()
}
}
}
그리고 이렇게 view에서 viewModel의 fetchPeopleNumber을 호출하면 끝!
이렇게 기본적인 shared session을 사용해 보았고, 다음 글에서는 configuration을 구성해서 delegate 방식으로 데이터를 받아와보자!
Apple 공식 문서 - Fetching website data into memory
Apple 공식 문서 - URLSession