훌이
후리스콜링개발
훌이

블로그 메뉴

  • 왈 (iOS APP)
  • Github
전체 방문자
오늘
어제
  • 전체 (171)
    • ⭐️ 개발 (140)
      • JAVA (4)
      • Web (5)
      • iOS & Swift (94)
      • iOS Concurrency (4)
      • Rx (18)
      • Git (6)
      • WWDC (1)
      • Code Refactor (3)
      • Server (1)
    • ⭐️ Computer Science (22)
      • 운영체제 (10)
      • 네트워크 (5)
      • PS (7)
    • 경제시사상식 (8)
    • 기타 등등 (0)

인기 글

최근 글

07-09 00:44

티스토리

hELLO · Designed By 정상우.
훌이

후리스콜링개발

[iOS] URLSession(1)
⭐️ 개발/iOS & Swift

[iOS] URLSession(1)

2022. 8. 30. 13:01
728x90
반응형

URLSession 

요청을 하는 하나하나가 Task라고 해서 URLSessionTask

CompletionHandler를 통해서 클로저 구문에서 네트워크 통신이 일어나게 됨

 


[1] URLSession

일반적으로는 shared를 가장 많이 쓰는데 default 값이라 응답은 클로저로 받고, 커스텀이 불가능해서 백그라운드 다운로드가 불가하다.

 

만약 커스텀을 하고 싶다면, 초기화 구문의 configuration을 통해서 가능하다.

 

< configuration 종류 >

1). 기본값은 default인데 URLSession.shared과 설정이 유사하지만

- 커스텀이 가능하고(셀룰러 연결 여부를 캐치해서 사용자한테 "너 LTE 쓸 건데 괜찮아?"라고 물어볼 수 있음,

- timeout 간격(ex. 서버에게 요청하고 5초가 지났는데 응답이 오지 않으면 사용자에게 "다시 시도해달라"라는 메시지를 보내는 것) 등을 설정할 수 있음),

- 응답은 클로저와 딜리게이트 둘 다에서 받을 수 있다.

 

2). 보안과 관련해서 정보를 휘발시킬 수 있는 ephemeral

 

3). 사용자가 앱을 사용하고 있지 않더라도 백그라운드에서 작업이 가능한 background

 

 

 


[2] Task

 

어떻게 request를 할 거냐에 따라 URLSessionTask 부분에서 결정

1). 목적에 따라 종류가 달라지는데, 가장 많이 사용하는 게 URLSessionDataTask

2). 넷플릭스에서 오프라인으로 저장하는 기능 같은 백그라운드 다운로드를 할 건지 URLSessionBackgroundTask

크롬의 secret mode 같은 정보가 남지 않는 경우에서 할 건지 

3). 구글드라이브에 기획서와 같은 파일을 업로드하고 싶을 때는 URLSessionUploadTask

 

dataTask 부분이 알라모에서 request 메소드 부분과 같음

 

 


[3] Response

1). handler - 클로저를 통해서

응답이 빨라서 간단한 처리인 경우

 

2). URLSessionDelegate / URLSessionTaskDelegate / URLSessionDataDelegate

10초가 걸리는 네트워크 통신이 있는데, 요청하고 응답하는 시간 동안 10초 내내 기다려야 했는데 

이걸 통해서 프로그래스바나 그 사이에 발생하는 에러 등을 추가로 덧붙이기 위해서 사용할 때 사용하는 것이 Delegate

 


왜 클로저로 2개의 값을 보내는 걸까?

통신이 성공해서 데이터를 받아올 수도 있지만, 실패할 가능성도 있기 때문에 APIError?를 보내는 것이다.

스위프트에서 말하는 에러 핸들링 방법에서의 그 Error 프로토콜...!

 

 


URLSession에서 네트워크 통신을 하는 순간 background thread로 동작하도록 만든다.

main이 아니라 global로 작업을 하는 것임

 

global queue에서 처리되는 네트워크 통신 작업

 

 

따라서 뷰컨에서 UI 관련 작업은 main thread에서 하라는 경고창이 뜰 거다.

그래서 해당 API 코드를 필요할 때마다 호출하면 DispatchQueue.main.async { ~ } 의 반복이 나타날 건데...

 

애초에 통신 할 때 클로저 구문에서 main 스레드로 비동기처리를 해주면 된다.

 

main queue에서 처리되도록 변경

 

 


URLComponent

url을 URLComponent라는 구조체를 통해서 다른 형식으로 써줄 수 있다.

 

 

이건 또 제법 신기하다. 네트워크 공부하면서 나온 개념들 scheme, host, path, query,,


반복되는 APIManager 코드를 개선해보기

매번 APIManager를 만들 때마다 같은 코드가 반복되는 것을 개선시켜주기 위해서 아래와 같이 함수를 만들어준다.

extension URLSession {
    
    typealias completionHandler = (Data?, URLResponse?, Error?) -> Void
    
    @discardableResult
    func customDataTask(_ endpoint: URLRequest,
                        completionHandler: @escaping completionHandler) -> URLSessionDataTask {
        
        let task = dataTask(with: endpoint, completionHandler: completionHandler)
        task.resume()
        return task
        
    }
        
    // T에는 string, int 이런 것들이 들어가는 게 아니라 Codable을 채택한 아이들만 들어갈 수 있음
    static func request<T: Codable>(_ session: URLSession = .shared,
                                    endpoint: URLRequest,
                                    completion: @escaping (T?, APIError?) -> Void) {
        
        session.customDataTask(endpoint) { data, response, error in
            DispatchQueue.main.async {
                guard error == nil else {
                    print("Failed Request")
                    completion(nil, .failedRequest)
                    return
                }
                
                guard let data = data else {
                    print("No Data Returned")
                    completion(nil, .noData)
                    return
                }

                guard let response = response as? HTTPURLResponse else {
                    print("Unable Response")
                    completion(nil, .invalidResponse)
                    return
                }

                guard response.statusCode == 200 else {
                    print("Failed Response")
                    completion(nil, .failedRequest)
                    return
                }
                
                do {
                    let result = try JSONDecoder().decode(T.self, from: data)
                    print(result)
                    completion(result, nil)
                } catch {
                    print(error)
                    completion(nil, .invalidData)
                }
            }
        }
    }
}

 

 


하나하나씩 먼저 이해해보자면,

 

✔️1단계)

 

 

기존 URLSession 코드는 아래와 같다.

- URLSession.shared를 받는 매개변수로 session이 들어가고

 

 

 

✔️2단계)

그 뒷부분의 dataTask(with: ~, completionHandler: ~)을 대신해서 아래 customDataTask 함수를 만들어준다.

 

 

customDataTask 함수는

- URLReqeust를 받는 매개변수로 endpoint

- completionHandler를 받는 매개변수로 completionHandler (= typealias로 (Data?, URLResponse?, Error?) -> Void 임)

 

 

 

반환값은 URLSessionDataTask이기 때문에 1단계에서 session 뒤에 붙을 수 있는 거다.

근데 왜 customDataTask를 안써주냐고 경고창이 뜨는데 

 

 

반환값을 안쓰겠다는 의미에서 @discardableResult를 달아주면 된다.

 

 

✔️3단계)

다시 request 함수로 돌아와서, 기존 APIManager에서 작성해준 DispatchQueue.main.async { ~ } 부분을 그대로 긁어다가 

붙여넣어주면 된다.

 

근데 고쳐줘야 하는 지점은 모델과 completionHandler

 

LottoAPIManager라면 (Lotto?, APIError?) -> Void

PersonAPIManager라면 (Person?, APIError?) -> Void

모델 부분을 제네릭하게 만들어줘야 함

 

1). 모델은 Codable을 채택한 타입들만 들어갈 수 있기 때문에 Codable을 채택시킨 제네릭 타입을 넣어주고 (T.self)

2). completionHandler 부분에 알맞게 (T?, APIError?) -> Void 를 넣어준다.

 

 

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'⭐️ 개발 > iOS & Swift' 카테고리의 다른 글

[iOS] 다국어 지원 i18n, l10n  (1) 2022.09.06
[iOS] 안간단한 MVVM 안간단하게 톺아보기  (3) 2022.08.31
[Swift] Codable  (2) 2022.08.29
[Swift] Error Handling  (4) 2022.08.26
[Swift] WMO - Swift 성능 최적화  (1) 2022.08.26
    '⭐️ 개발/iOS & Swift' 카테고리의 다른 글
    • [iOS] 다국어 지원 i18n, l10n
    • [iOS] 안간단한 MVVM 안간단하게 톺아보기
    • [Swift] Codable
    • [Swift] Error Handling
    훌이
    훌이

    티스토리툴바