상세 컨텐츠

λ³Έλ¬Έ 제λͺ©

[URLSession] completion(nil) λŒ€μ‹  Result νƒ€μž… μ‚¬μš©ν•΄λ³΄κΈ°

🍎 iOS/Network

by AHN.Jihyeon 2024. 8. 2. 09:22

λ³Έλ¬Έ

Result νƒ€μž…μ€ 성곡과 μ‹€νŒ¨ 두 가지 κ°€λŠ₯ν•œ κ²°κ³Όλ₯Ό λ‚˜νƒ€λ‚΄κΈ° μœ„ν•΄ μ‚¬μš©λœλ‹€.

Result νƒ€μž…μ€ μ—΄κ±°ν˜•μœΌλ‘œ, 성곡 μ‹œμ—λŠ” 값을 λ°˜ν™˜ν•˜κ³  μ‹€νŒ¨ μ‹œμ—λŠ” 였λ₯˜λ₯Ό λ°˜ν™˜ν•œλ‹€.

 

enum Result<Success, Failure> where Failure : Error {
    case success(Success)
    case failure(Failure)
}

Success(μ—°κ΄€κ°’): μž‘μ—…μ΄ μ„±κ³΅ν–ˆμ„ λ•Œ λ°˜ν™˜λ˜λŠ” κ°’μ˜ νƒ€μž….

Failure(μ—°κ΄€κ°’): μž‘μ—…μ΄ μ‹€νŒ¨ν–ˆμ„ λ•Œ λ°˜ν™˜λ˜λŠ” 였λ₯˜μ˜ νƒ€μž…. FailureλŠ” λ°˜λ“œμ‹œ Error ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•΄μ•Ό ν•œλ‹€.

 

 

 

κ·Έλ ‡λ‹€λ©΄ Result νƒ€μž…μ€ μ™œ μ“°λŠ”κ±΄λ°?

  • λͺ…ν™•ν•œ 였λ₯˜ 처리: Result νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ 성곡과 μ‹€νŒ¨λ₯Ό λͺ…ν™•ν•˜κ²Œ ꡬ뢄할 수 μžˆλ‹€.
  • 가독성 ν–₯상: μ½”λ“œμ˜ 가독성이 ν–₯μƒλ˜κ³ , 였λ₯˜ 처리 둜직이 간결해진닀.
  • νƒ€μž… μ•ˆμ „μ„±: Result νƒ€μž…μ€ μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜λ―€λ‘œ, 성곡과 μ‹€νŒ¨μ— λŒ€ν•œ νƒ€μž…μ„ λͺ…ν™•ν•˜κ²Œ 지정할 수 μžˆλ‹€.

 

 

Result νƒ€μž…μ˜ μ‚¬μš© 예제

기쑴의 completion ν΄λ‘œμ €λ₯Ό Result νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•˜μ—¬ μ‚¬μš©ν•΄λ³΄κΈ° 

 

1. κΈ°μ‘΄ completion ν΄λ‘œμ €

기쑴의 completion ν΄λ‘œμ €λŠ” ([Movie]?) -> Void ν˜•νƒœλ‘œ μ •μ˜λ˜μ—ˆλ‹€. 

func fetchMovies(completion: @escaping ([Movie]?) -> Void) {
    // λ„€νŠΈμ›Œν¬ μš”μ²­ 및 응닡 처리 둜직
}

 

 

 

2. Result νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” completion ν΄λ‘œμ €

// NetworkError μ •μ˜
enum NetworkError: Error {
    case invalidURL
    case noData
}

func fetchMovies(completion: @escaping (Result<[Movie], Error>) -> Void) {
    
    guard let url = URL(string: "https://api.example.com/movies") else {
        completion(.failure(NetworkError.invalidURL)) // μœ νš¨ν•˜μ§€ μ•Šμ€ URL 였λ₯˜ 전달
        return
    }

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            completion(.failure(error)) // λ„€νŠΈμ›Œν¬ 였λ₯˜ 전달
            return
        }
        
        guard let data = data else {
            completion(.failure(NetworkError.noData)) // 데이터가 μ—†λŠ” 경우 였λ₯˜ 전달
            return
        }
        
        // JSON λ””μ½”λ”©
        do {
            let decoder = JSONDecoder()
            let result = try decoder.decode(MovieResponse.self, from: data)
            completion(.success(result.results)) // μ„±κ³΅μ μœΌλ‘œ λ””μ½”λ”©λœ μ˜ν™” λͺ©λ‘ 전달
        } catch let jsonError {
            completion(.failure(jsonError)) // JSON λ””μ½”λ”© 였λ₯˜ 전달
        }
    }
    
    task.resume()
}

 

βœ… completion(.failure(NetworkError.invalidURL))와 같이 .failure둜 μ ‘κ·Όν•  수 μžˆλŠ” 이유?

Result νƒ€μž…μ΄ μ—΄κ±°ν˜•(enum)으둜 μ •μ˜λ˜μ–΄ 있기 λ•Œλ¬Έμ΄λ‹€.

μ—΄κ±°ν˜• μ΄λ¦„μ˜ νƒ€μž…μΈ Result μƒλž΅ κ°€λŠ₯.

 

  

 

βœ… completion(.success(result.results))μ—μ„œ resultsλŠ” result 객체의 속성!

struct MovieResponse: Decodable {
    let results: [Movie]
}

MovieResponseλŠ” API 응닡 데이터λ₯Ό λ‚˜νƒ€λ‚΄λŠ” ꡬ쑰체닀.

 

MovieResponse κ΅¬μ‘°μ²΄λŠ” resultsλΌλŠ” 속성을 가지고 있고, μ΄λŠ” μ˜ν™” λͺ©λ‘μ„ λ‚˜νƒ€λ‚Έλ‹€.

fetchMovies ν•¨μˆ˜ λ‚΄μ—μ„œ JSON 데이터λ₯Ό λ””μ½”λ”©ν•  λ•Œ, MovieResponse 객체가 μƒμ„±λœλ‹€.

 

 

 

 

 

3. λ·° μ»¨νŠΈλ‘€λŸ¬μ—μ„œ fetchMovies μ‚¬μš©ν•˜κΈ°

fetchMovies ν•¨μˆ˜λ₯Ό λ·° μ»¨νŠΈλ‘€λŸ¬μ—μ„œ ν˜ΈμΆœν•˜κ³ , λ„€νŠΈμ›Œν¬ μš”μ²­μ„ 톡해 데이터λ₯Ό 받아와

ν…Œμ΄λΈ” λ·°λ₯Ό μ—…λ°μ΄νŠΈν•  λ•Œ μ•„λž˜μ™€ 같이 μ‚¬μš©ν•  수 μžˆλ‹€.

import UIKit

class MoviesViewController: UIViewController, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!
    
    var movies: [Movie] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        
        // fetchMovies ν•¨μˆ˜ 호좜
        fetchMovies { result in
            DispatchQueue.main.async {
                switch result {
                case .success(let movies):
                    self.movies = movies
                    self.tableView.reloadData() // ν…Œμ΄λΈ” λ·° μ—…λ°μ΄νŠΈ
                case .failure(let error):
                    // 였λ₯˜ 처리
                     switch error {
       				 case NetworkError.invalidURL:
         			     print("Invalid URL error occurred.")
      				 case NetworkError.noData:
        			     print("No data returned from the server.")
    				 default:
            		     print("An error occurred: \(error.localizedDescription)")
                     }
                }
            }
        }
    }
    
    // UITableViewDataSource λ©”μ„œλ“œ κ΅¬ν˜„
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return movies.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MovieCell", for: indexPath)
        let movie = movies[indexPath.row]
        cell.textLabel?.text = movie.title
        return cell
    }
    
    // 였λ₯˜ μ•Œλ¦Ό ν‘œμ‹œ λ©”μ„œλ“œ
    private func showAlert(for error: Error) {
        let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

completion ν΄λ‘œμ €μ˜ 인자둜 resultκ°€ μ „λœλ‹€. resultλŠ” Result<[Movie], Error> νƒ€μž…μ΄λ‹€.

switch resultλ₯Ό 톡해 result의 성곡(.success)κ³Ό μ‹€νŒ¨(.failure)λ₯Ό κ΅¬λΆ„ν•œλ‹€.

κ΄€λ ¨κΈ€ 더보기