본문 바로가기

학습 노트/iOS (2021)

219. Task Scheduling

Task Scheduling


Task는 처음 생성하면 별다른 동작 없이 대기하고 있다가 resume 메서드가 호출되면 즉시 동작을 시작한다.
이는 사용자에게 즉시 필요한 데이터라면 적합한 작동 방식이다.

하지만 지금 당장 데이터가 필요한 경우가 아니라면 미리 기기의 용량을 차지하거나,
셀룰러 용량을 낭비하게 된다.

Task Scheduling은 iOS가 Task의 정보를 저장하고 있다가 지정된 시점에 자동으로 다운로드하는 방식으로 동작한다.
따라서 Task의 주체가 앱이 아니게 되고, Background Session 에서만 구현할 수 있다.

@IBAction func startDownload(_ sender: Any) {
  guard let url = URL(string: bigFileUrlStr) else {
     fatalError("Invalid URL")
  }

  task = session.downloadTask(with: url)

   task?.earliestBeginDate = Date(timeIntervalSinceNow: 5)

  task?.resume()
}

startDownload 메서드는 session을 사용해 task를 생성한다.
earliestBeginDate 속성을 지정하면 해당 시간이 되기 이전에는 절대로 Task가 실행되지 않는다.

@IBAction func startDownload(_ sender: Any) {
  guard let url = URL(string: bigFileUrlStr) else {
     fatalError("Invalid URL")
  }

  task = session.downloadTask(with: url)

   task?.earliestBeginDate = Date(timeIntervalSinceNow: 5)
   task?.countOfBytesClientExpectsToSend = 80
   task?.countOfBytesClientExpectsToReceive = 1024 * 1024 * 40

  task?.resume()
}

그다음 설정하는 속성은 countOfBytesClientExpectsToSend와 countOfBytesClientExpectsToReceive이다.

countOfBytesClientExpectsToSend

 

Apple Developer Documentation

 

developer.apple.com

 

countOfBytesClientExpectsToReceive

 

Apple Developer Documentation

 

developer.apple.com

두 속성은 각각 Client가 Host에게 전송하는 요청의 크기와
Host에서 다운로드하여올 데이터의 크기를 의미하는데 이 두 가지 데이터를 사용해
iOS는 Task에 필요한 최적의 시간을 계산해 네트워크 최적화를 진행한다.

lazy var session: URLSession = { [weak self] in
  let config = URLSessionConfiguration.background(withIdentifier: "SampleSession")

   config.isDiscretionary = true

  let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue.main)
  return session
}()

SessionConfiguration의 isDidcretionary 속성을 true로 변경해
해당 Task의 시작 시점에 대한 재량권을 iOS에게 위임한다.
이 방식은 작은 데이터 보다는 큰 데이터에서 유용하다.

extension TaskSchedulingViewController: URLSessionDownloadDelegate {
    func urlSession(_ session: URLSession, task: URLSessionTask, willBeginDelayedRequest request: URLRequest, completionHandler: @escaping (URLSession.DelayedRequestDisposition, URLRequest?) -> Void) {
        DispatchQueue.main.async {
            self.sizeLabel.text = "Start"
        }
        completionHandler(.continueLoading, nil)
    }
}

SessionDelegate에서 메서드를 하나 추가한다.

urlSession(_:task:willBeginDelayedRequest:completionHandler:)

 

Apple Developer Documentation

 

developer.apple.com

해당 메서드는 지연 Task가 실행되기 직전에 호출된다.
UI의 업데이트 보다는 Task 처리 방식의 변경을 목적으로 사용하는 경우가 많다.
(구독 플랜의 취소, 서버 문제로 인한 url 업데이트...)

URLSession.DelayedRequestDisposition

 

Apple Developer Documentation

 

developer.apple.com

CompletionHandler의 첫 번째 파라미터로 전달되는 DelayedRequestDisposition은 세 가지 상태를 가진다.

  • cancel
    취소
  • continueLoading
    변경 없이 사용하는 경우
  • useNewRequest
    새로운 요청을 사용해야 하는 경우

두번째 파라미터인 URLRequest는 DelayedRequestDisposition이 useNewRequest인 경우에
새로운 request를 전달하는 용도로 사용한다.

실습에는 DelayedRequestDisposition으로 continueLoading을,  URLRequest는 nil을 사용했다.

지금은 5초로 짧게 설정해서 문제가 존재하지 않지만,
실제로는 Timeout을 함께 사용하는 경우를 고려해야 한다.

Timeout을 설정하는 RequestTimeout과 ResourceTimeout은
각각 최초 응답과 Task의 완료 시점을 기준으로 Timeout을 판단하며,
기본값은 RequestTimeout이 60초, ResourceTimeout은 7일이다.

따라서 Timeout을 Delay 이후로 설정하거나 Delay를 Timeout 이전에 실행해야 한다.
단, Timeout은 Session 생성 이전에 설정해야 하며, 동작 시간을 고려해 여유 있게 설정해야 함을 잊지 말아야 한다.

'학습 노트 > iOS (2021)' 카테고리의 다른 글

218. Adaptable Connectivity  (0) 2022.09.07
217. Cellular Connection  (0) 2022.09.07
216. Reachability  (0) 2022.09.06
215. Response Caching  (0) 2022.09.06
214. Background Download  (0) 2022.08.31