'connectionWithRequest:delegate:' is deprecated: first deprecated in iOS 9.0 - Use NSURLSession (see NSURLSession.h)
URLSession은 iOS 7.0부터 도입되었지만 iOS 9.0부터 NSURLConnection의 대체제로 사용되기 시작한다.
URLSession으로 대체된 주된 이유는 URLSession이 제공하는 개선된 기능과 유연한 네트워킹 아키텍쳐 때문이다.
- 백그라운드에서의 네트워킹 지원
- Session 기반의 구성
- 더 나아진 멀티테스킹과 성능
- 다운로드 및 업로드 작업 관리
- 더 풍부한 API 자원
- 보안 및 최신 기능
URLSession은 앱이 백그라운드에서 실행될 때도 데이터를 전송하거나 받을 수 있는 기능을 제공한다.
이는 대용량 파일을 다운로드하거나 업로드할 때 특히 유용하다.
NSURLConnection은 이러한 기능을 지원하지 않아, 앱이 활성 상태일 때만 데이터 통신이 가능했다.
URLSession은 멀티태스킹을 더 효율적으로 지원하며, 특히 복잡한 네트워크 요청을 처리하는 데 있어 더 나은 성능을 제공한다.
- 그룹화된 네트워크 작업 관리
- 고유한 세션 구성
- 세션의 유연성
- 효율적인 자원 관리
좀더 설명하자면,
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&count=3")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error: \(error)")
return
}
if let data = data {
// 데이터 사용
// print("Data: \(data)")
let receivedDataString = String(data: data as Data,
encoding: .utf8) ?? "Invalid data encoding"
print("Received Data: \(receivedDataString)")
}
}
task.resume()
URLSession의 클로저는 기본적으로 비동기적인 동작을 한다.
그래서 각 dataTask는 별도의 스레드에서 병렬적으로 실행이 된다.
그리고 dataTask가 완료되면 콜백 함수를 호출해 데이터, 응답, 에러 등을 처리할 수 있게 된다.
let session = URLSession.shared
let url1 = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&count=1")!
let url2 = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&count=1")!
let task1 = session.dataTask(with: url1) { data, response, error in
if let error = error {
print("Error in task 1: \(error)")
return
}
if let data = data, let string = String(data: data, encoding: .utf8) {
print("Response from task 1: \(string)")
}
}
let task2 = session.dataTask(with: url2) { data, response, error in
if let error = error {
print("Error in task 2: \(error)")
return
}
if let data = data, let string = String(data: data, encoding: .utf8) {
print("Response from task 2: \(string)")
}
}
task1.resume()
task2.resume()
특정 요구 사항에 맞게 세션을 구성할 수 있다.
예를 들어, 타임아웃 설정, 캐시 정책, 셀룰러 네트워크 접근 등을 세션별로 다르게 설정할 수 있다.
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 20.0 // 20초 타임아웃
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData // 캐시 무시
configuration.allowsCellularAccess = false // 셀룰러 네트워크 사용 금지
let customSession = URLSession(configuration: configuration)
let task = customSession.dataTask(with: URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY&count=3")!) { data, response, error in
// 데이터 처리
}
task.resume()
다양한 상황에 맞게 여러 세션을 생성하고 사용할 수 있다.
// 셀룰러 네트워크를 사용하는 세션
let cellularConfiguration = URLSessionConfiguration.default
cellularConfiguration.allowsCellularAccess = true
let cellularSession = URLSession(configuration: cellularConfiguration)
// 셀룰러 네트워크를 사용하지 않는 세션
let wifiConfiguration = URLSessionConfiguration.default
wifiConfiguration.allowsCellularAccess = false
let wifiSession = URLSession(configuration: wifiConfiguration)
세션 구성을 통해 네트워크 자원을 효율적으로 관리한다.
예를 들어, 백그라운드 세션을 사용하여 앱이 백그라운드 상태일 때도 데이터를 처리할 수 있다.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("다운시작")
let backgroundConfiguration = URLSessionConfiguration.background(withIdentifier: "com.example.myapp.background")
backgroundConfiguration.isDiscretionary = true // 시스템에 의해 최적의 시간에 실행
// let backgroundSession = URLSession(configuration: backgroundConfiguration)
let backgroundSession = URLSession(configuration: backgroundConfiguration, delegate: self, delegateQueue: OperationQueue())
let downloadTask = backgroundSession.downloadTask(with: URL(string: "https://apod.nasa.gov/apod/image/0810/lagoon_hst.jpg")!)
downloadTask.resume()
}
}
extension ViewController: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("다운완료")
guard let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let destinationURL = documentsDirectoryURL.appendingPathComponent(downloadTask.response?.suggestedFilename ?? "default_filename")
do {
// 기존에 동일한 파일이 있으면 삭제
if FileManager.default.fileExists(atPath: destinationURL.path) {
try FileManager.default.removeItem(at: destinationURL)
}
// 파일을 영구적인 위치로 이동
try FileManager.default.moveItem(at: location, to: destinationURL)
print("File moved to Documents folder: \(destinationURL)")
} catch {
print("File move error: \(error)")
}
DispatchQueue.main.async { [weak self] in
let alert = UIAlertController(title: "Download Completed", message: "Your file has been downloaded.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
self?.present(alert, animated: true)
}
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print("다운로드 에러: \(error)")
}
}
}
URLSessionConfiguration.background를 사용하는 백그라운드 세션에서 파일을 다운로드하는 경우, 다운로드가 완료된 후에 실행될 콜백을 구현하기 위해서는 URLSessionDownloadDelegate 프로토콜을 사용해야 한다.
백그라운드 세션은 앱이 백그라운드 상태에 있거나 종료된 후에도 작업을 완료할 수 있으며,
완료 시 다운로드 관련 이벤트를 처리하기 위한 델리게이트 메서드를 호출한다.
이 클래스는 네트워크 연결을 설정하고, 데이터를 전송하며, 서버로부터의 응답을 받는 데 사용되었다.
- 서버로부터 응답을 받기 시작할 떄 호출된다.
- 데이터의 일부가 서버로 부터 수신될 때마다 호출된다.
- 모든 데이터가 성공적으로 수신되었을 때, 호출된다.
- 연결실패, 네트워크 오류, 타임아웃 등 다양한 이유로 인해서도 호출 될 수 있다.
- Error 객체를 통해 어떤 문제가 발생했는지 확인할 수 있다.
class ViewController: UIViewController {
var responseData: NSMutableData?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemRed
// URL을 설정합니다.
if let url = URL(string: APIService.apiURL) {
// NSURLRequest 객체를 생성합니다.
let request = URLRequest(url: url)
// NSURLConnection을 이용해 요청을 보냅니다.
let connection = NSURLConnection(request: request, delegate: self)
// 응답 데이터를 위한 초기화
responseData = NSMutableData()
}
}
}
extension ViewController: NSURLConnectionDataDelegate {
// 데이터가 수신시작시 호출된다.
func connection(_ connection: NSURLConnection, didReceive response: URLResponse) {
// 데이터 초기화
responseData?.length = 0
}
// 데이터의 조각이 들어올 때마다 호출된다.
func connection(_ connection: NSURLConnection, didReceive data: Data) {
responseData?.append(data)
}
// 데이터 수신 완료
func connectionDidFinishLoading(_ connection: NSURLConnection) {
if let data = responseData {
do {
// MARK: 받은 데이터를 문자열로 변환하여 출력 (디버깅용)
let receivedDataString = String(data: data as Data,
encoding: .utf8) ?? "Invalid data encoding"
print("Received Data: \(receivedDataString)")
// MARK: 받은 데이터를 Codable을 통해 파싱
// if let data = responseData {
// do {
// // JSON 데이터를 APOD 배열로 파싱
// let apods = try JSONDecoder().decode([APOD].self, from: data as Data)
// // 파싱된 데이터 출력
// for apod in apods {
// print("""
// ------------------------
// - Title: \(apod.title)
// - Date: \(apod.date)
// - URL: \(apod.imgURLStr)
// """)
// }
// } catch {
// print("JSON parsing error: \(error)")
// }
// }
// MARK: JSON 파싱
// if let jsonArray = try JSONSerialization.jsonObject(with: data as Data, options: []) as? [[String: Any]] {
// for item in jsonArray {
// if let title = item["title"] as? String,
// let date = item["date"] as? String,
// let url = item["url"] as? String {
// print("""
// ------------------------
// - Title: \(title)
// - Date: \(date)
// - URL: \(url)
// """)
// }
// }
// }
// } catch let error as NSError {
// print("Error parsing JSON: \(error.localizedDescription)")
}
}
}
// 에러가 발생했을 때 호출된다.
func connection(_ connection: NSURLConnection, didFailWithError error: Error) {
print("Connection failed with error: \(error.localizedDescription)")
}
}
class APIService {
static let apiKey = "DEMO_KEY"
static let fetchCount = 2
static let apiURL = "https://api.nasa.gov/planetary/apod?api_key=\(apiKey)&count=\(fetchCount)"
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// URL 설정
if let url = URL(string: APIService.apiURL) {
// URLSession을 사용한 데이터 요청
let task = URLSession.shared.dataTask(with: url) { data, response, error in
// 에러 처리
if let error = error {
print("Error: \(error.localizedDescription)")
return
}
// 데이터 처리
if let data = data {
self.handleReceivedData(data)
}
}
// 요청 시작
task.resume()
}
}
// 받은 데이터 처리
private func handleReceivedData(_ data: Data) {
do {
// MARK: 받은 데이터를 문자열로 변환하여 출력 (디버깅용)
let receivedDataString = String(data: data, encoding: .utf8) ?? "Invalid data encoding"
print("Received Data: \(receivedDataString)")
// MARK: Codable을 이용한 JSON 파싱
// do {
// // JSON 데이터를 APOD 배열로 파싱
// let apods = try JSONDecoder().decode([APOD].self, from: data as Data)
// // 파싱된 데이터 출력
// for apod in apods {
// print("""
// ------------------------
// - Title: \(apod.title)
// - Date: \(apod.date)
// - URL: \(apod.imgURLStr)
// """)
// }
// }
//
// } catch {
// print("JSON parsing error: \(error)")
// }
// MARK: JSONSerialization을 사용한 파싱
// if let jsonArray = try JSONSerialization.jsonObject(with: data as Data, options: []) as? [[String: Any]] {
// for item in jsonArray {
// if let title = item["title"] as? String,
// let date = item["date"] as? String,
// let url = item["url"] as? String {
// print("""
// ------------------------
// - Title: \(title)
// - Date: \(date)
// - URL: \(url)
// """)
// }
// }
// }
} catch {
print("Error parsing data: \(error)")
}
}
}
class APIService {
static let apiKey = "DEMO_KEY"
static let fetchCount = 2
static let apiURL = "https://api.nasa.gov/planetary/apod?api_key=\(apiKey)&count=\(fetchCount)"
}
// APOD 구조체 정의
struct APOD: Codable {
let title: String
let date: String
let description: String
let type: String
let imgURLStr: String
enum CodingKeys: String, CodingKey {
case title
case date
case description = "explanation"
case type = "media_type"
case imgURLStr = "url"
}
}
NSURLSession과 NSURLConnection의 주요 차이점 NSURLConnection : NSURLConnection으로 네트워크 연결을 맺고 있다가 만약 시스템이 인터럽트를 걸었다고 가정합시다. 그러면 앱이 백그라운드로 가게 되고 앱에서 전송받고 있던 모든데이터가 날아가게 됩니다.
NSURLSession : 이러한 문제를 해결해주고 다운로드 작업자체에서 우리를 해방시켜줬습니다. NSURLSession은 네트워크연결을 알아서 관리해줍니다. AppDelegate에서 application:handleEventsForBackgroundURLSession:completionHandler 메소드를 구현하셔서 백그라운드 다운로드도 직접 관리해줄 수 있습니다.**그러니까 NSURLSession을 사용하시면 네트워크 연결을 체크하실 필요가 없습니다. OS가 알아서 해주거든요.