관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 3. Multipart, 멀티파트 - BackgroundSession 사용 방법 (background에서 업로드, suspended에서 업로드, not running에서 업로드, getTasksWithCompletionHandler) 본문

iOS 응용 (swift)

[iOS - swift] 3. Multipart, 멀티파트 - BackgroundSession 사용 방법 (background에서 업로드, suspended에서 업로드, not running에서 업로드, getTasksWithCompletionHandler)

jake-kim 2023. 7. 3. 02:20

1. Multipart, 멀티파트 - 개념 (메모리 효율성, 네트워크 대역폭, 개별적 재전송 유리)

2. Multipart, 멀티파트 - URLSession으로 구현 (uploadTask, URLSessionUploadTask, progress 처리)

3. Multipart, 멀티파트 - BackgroundSession 사용 방법 (background에서 업로드, suspended에서 업로드, not running에서 업로드, getTasksWithCompletionHandler)

BackgroundSession 이란?

  • URLSession 인스턴스를 생성할 때 URLSessionConfiguration를 사용하여 캐시 정책, 타임아웃 지정이 가능한데 여기서 background session 컨피그로 설정한 것
  • URLSessionConfiguration에 관한 구체적인 내용은 이 포스팅 글에서 확인
  • BackgroundSession으로 설정하면 default 컨피그와 동일하게 7일 timeout으로 지정됨 (timeoutIntervalForResponse)
    • timeoutIntervalForResponse값은 언제든 변경 가능
let configuration = URLSessionConfiguration.background(withIdentifier: "test")
print(configuration.timeoutIntervalForResource) // 604800초 = 7일

BackgroundSession 동작 이해하기

  • background session은 별도의 프로세스에서 전송을 처리되는 시스템
  • iOS 앱 자체가 일시 중단되거나 종료된 경우, 앱을 다시 켰을때 재전송이 기능
  • iOS 앱이 시스템에 의해 종료되고 다시 시작되면 앱은 identifier로 구분하여 session을 다시 생성한 후 종료 시점에 진행 중이던 전송 상태를 검색하여 다시 재개가 가능
    • 단, 시스템에서 앱을 정상적으로 종료하는 경우만 적용
    • 사용자가 멀티태스킹 화면에서 앱을 종료하면 시스템은 세션의 백그라운드 전송을 모두 취소하므로 주의
  • 시스템은 사용자가 강제로 종료한 앱을 자동으로 다시 시작하지 않음 (전송을 다시 시작하려면 사용자가 명시적으로 앱을 다시 시작해야함)

BackgroundSession을 사용하여 이미지 업로드하기

  • 이전 포스팅 글에서 알아본 대로 멀티파트 방식을 사용하기 위해 body부분에 특정 형태의 문자열을 채워넣기
  • backgroundSession을 사용하는 부분은 URLSession.shared를 사용하지 않고 URLSession을 만들때 background 컨피그를 사용한다는것만 차이
func uploadImageUsingURLSession(imageData: Data, completion: @escaping (Error?) -> Void) {
    let url = URL(string: "https://example.com/upload")
    
    var request = URLRequest(url: url!)
    request.httpMethod = "POST"
    
    let uniqString = UUID().uuidString
    let contentType = "multipart/form-data; boundary=\(uniqString)"
    request.setValue(contentType, forHTTPHeaderField: "Content-Type")
    
    var body = Data()
    
    body.append("--\(uniqString)\r\n".data(using: .utf8)!)
    body.append("Content-Disposition: form-data; name=\"image\"; filename=\"image.jpg\"\r\n".data(using: .utf8)!)
    body.append("Content-Type: image/jpeg\r\n\r\n".data(using: .utf8)!)
    body.append(imageData)
    body.append("\r\n".data(using: .utf8)!)
    body.append("--\(uniqString)--\r\n".data(using: .utf8)!)
    
    let session = URLSession(configuration: .background(withIdentifier: id))
    let task = session.uploadTask(with: request, from: body) { (data, response, error) in
        DispatchQueue.main.async {
            if let error = error {
                completion(error)
                return
            }
            // 응답 처리
            
            completion(nil)
        }
    }
    
    task.delegate = self
    
    task.resume()
}

사용자가 앱 종료하고 다시 앱 실행 시킨 경우, 재개하기

  • backgroundSession을 만들 때의 id를 똑같이하여 session을 생성
    • backgroundSession에서만 id값을 필수로 입력하도록 인터페이스를 가지고 있는 이유는 이처럼 작업을 다시 재개할 수 있도록 하기 위함
  • session의 메소드인 getTasksWithCompletionHandler 사용
let configuration = URLSessionConfiguration.background(withIdentifier: "test_id")
let session = URLSession(configuration: configuration)

// 이전에 실행했던 이미지 업로드 작업을 확인하고 처리
session.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) in
    // uploadTasks에서 이전에 실행한 업로드 작업을 찾아서 처리
}
  • getTasksWithCompletionHandler 클로저 부분에서는 단순히 url을 비교하고 resume이나 calcel처리
session.getTasksWithCompletionHandler { (dataTasks, uploadTasks, downloadTasks) in
    for task in uploadTasks {
        if let originalRequest = task.originalRequest, let url = originalRequest.url {
            if url.absoluteString == "<이전에 실행한 업로드 작업의 URL>" {
                // 이전에 실행한 업로드 작업을 찾았을 때, 원하는 처리를 수행
                // 예를 들어, 업로드 작업을 이어서 진행하거나, 작업을 취소하고 처음부터 다시 시작할 수 있습니다.
                // task.resume() // 업로드 작업 이어서 진행
                // task.cancel() // 업로드 작업 취소
            }
        }
    }
}

정리

  • 사용자가 background로 갔을때도 계속 파일을 업로드나 다운로드 하고 싶은 경우 backgroundSession을 사용
  • 사용자가 앱 종료 후 다시 앱을 실행한 경우에도 backgroundSession을 사용했었다면 같은 identifer로 session을 만들면 이전 URLSessionTask 인스턴스를 획득이 가능 (그러므로 이전 task 들의 resume, cancel이 가능)

cf) 업로드 말고, 다운로드 하는 URLSessionDownloadTask를 사용하고 싶은 경우 이 포스팅 글 참고

 

* 전체 코드: https://github.com/JK0369/ExBackgroundSession

* 참고

https://developer.apple.com/documentation/foundation/urlsessionconfiguration/1407496-background

https://developer.apple.com/documentation/foundation/urlsessionconfiguration

Comments