- vừa được xem lúc

URLSession iOS

0 0 5

Người đăng: Huy Nguyen Duc

Theo Viblo Asia

Giới thiệu về Networking iOS:

1. Codable

Codable trong Swift là một protocol giúp tự động hoán đổi (encode/decode) dữ liệu giữa các kiểu dữ liệu Swift và các định dạng như JSON, plist, v.v.

Nó gồm 2 protocol con:

  • Encodable: cho phép chuyển đối tượng Swift thành dữ liệu (ví dụ: JSON).
  • Decodable: cho phép tạo đối tượng Swift từ dữ liệu.

Ví dụ:

struct User: Codable { let name: String let age: Int
}

Với User, bạn có thể dễ dàng:

  • Chuyển từ JSON sang User:
    let user = try JSONDecoder().decode(User.self, from: jsonData)
    
  • Chuyển từ User sang JSON:
    let jsonData = try JSONEncoder().encode(user)
    

2. URLSession

import Foundation struct MovieResponse: Codable { let results: [Movie]
} struct Movie: Codable { let title: String let overview: String let poster_path: String?
} func fetchPopularMovies(completion: @escaping ([Movie]) -> Void) { let apiKey = "YOUR_TMDB_API_KEY" let urlString = "https://api.themoviedb.org/3/movie/popular?api_key=\(apiKey)&language=en-US&page=1" guard let url = URL(string: urlString) else { print("❌ Invalid URL") return } var request = URLRequest(url: url) request.httpMethod = "GET" let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("❌ Request error: \(error.localizedDescription)") return } guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { print("❌ Server error") return } guard let data = data else { print("❌ No data received") return } do { let decoder = JSONDecoder() let movieResponse = try decoder.decode(MovieResponse.self, from: data) // **🧠 Update UI on main thread** DispatchQueue.main.async { completion(movieResponse.results) } } catch { print("❌ JSON decoding error: \(error.localizedDescription)") } } task.resume()
}

3. Triển khai trong dự án thực tế

Trong dự án thực tế, việc request api sẽ duplicate code nhiều, gây lãng phí thời gian, sau fix bug sẽ khó khăn hơn. Cách triển khai như sau

a. Tạo Enum để route các endpoint

import Foundation enum HTTPMethod: String { case get = "GET" case post = "POST"
} enum TMDBEndpoint { case popularMovies(page: Int) case topRatedMovies(page: Int) case searchMovies(query: String, page: Int) case movieDetail(id: Int) var baseURL: String { return "https://api.themoviedb.org/3" } var path: String { switch self { case .popularMovies: return "/movie/popular" case .topRatedMovies: return "/movie/top_rated" case .searchMovies: return "/search/movie" case .movieDetail(let id): return "/movie/\(id)" } } var method: HTTPMethod { return .get } var params: [String: String] { var baseParams: [String: String] = [ "api_key": "YOUR_TMDB_API_KEY", "language": "en-US" ] switch self { case .popularMovies(let page), .topRatedMovies(let page): baseParams["page"] = String(page) case .searchMovies(let query, let page): baseParams["query"] = query baseParams["page"] = String(page) case .movieDetail: break } return baseParams } func getURL() -> URL? { var components = URLComponents(string: baseURL + path) components?.queryItems = params.map { URLQueryItem(name: $0.key, value: $0.value) } return components?.url }
}

** b. Viết NetworkManager sử dụng generic:**

import Foundation final class NetworkManager { static let shared = NetworkManager() private init() {} func request<T: Decodable>( endpoint: TMDBEndpoint, responseType: T.Type, completion: @escaping (Result<T, Error>) -> Void ) { guard let url = endpoint.getURL() else { completion(.failure(NetworkError.invalidURL)) return } var request = URLRequest(url: url) request.httpMethod = endpoint.method.rawValue let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { DispatchQueue.main.async { completion(.failure(error)) } return } guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { DispatchQueue.main.async { completion(.failure(NetworkError.invalidResponse)) } return } guard let data = data else { DispatchQueue.main.async { completion(.failure(NetworkError.noData)) } return } do { let decodedData = try JSONDecoder().decode(T.self, from: data) DispatchQueue.main.async { completion(.success(decodedData)) } } catch { DispatchQueue.main.async { completion(.failure(error)) } } } task.resume() }
} // Define some simple errors
enum NetworkError: Error { case invalidURL case invalidResponse case noData
}

** c. Gọi API sử dụng generic:** Ví dụ fetch popular movies:

struct MovieResponse: Decodable { let results: [Movie]
} struct Movie: Decodable { let id: Int let title: String let overview: String
} NetworkManager.shared.request(endpoint: .popularMovies(page: 1), responseType: MovieResponse.self) { result in switch result { case .success(let response): print("🎬 Movies count: \(response.results.count)") case .failure(let error): print("❌ Error: \(error.localizedDescription)") }
}

Bình luận

Bài viết tương tự

- vừa được xem lúc

URLSession Networking trong Swift

Qua bài này, các bạn sẽ nắm được:. . Khi nào cần dùng URLSession. URLSession là gì.

0 0 66

- vừa được xem lúc

Hiển thị tiến độ download bằng URLSession

Cái này tưởng đơn giản mà lại làm mình mất khối thời gian, chỉ vì mình không biết đến việc cái này override cái kia, dẫn đến một số cái không chạy như ý. Do đó mình viết bài này để bạn nào chưa biết c

0 0 41