Networking & Codable
Cheat Sheet'lar
Networking & Codable Cheat Sheet
iOS ilovalarda REST API so'rovlarini yuborish, URLSession, xatolarni qayta ishlash va Codable orqali JSON bilan ishlash bo'yicha tezkor ma'lumotnoma.
1. URLSession & async/await Asoslari
func fetchPosts() async throws -> [Post] {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
throw URLError(.badURL)
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw URLError(.badServerResponse)
}
return try JSONDecoder().decode([Post].self, from: data)
}
2. URLRequest: POST, Headers va JSON Body
func createPost(post: PostCreateDTO, token: String) async throws -> Post {
let url = URL(string: "https://api.example.com/posts")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.timeoutInterval = 30 // soniya
// Sarlavhalar (Headers)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
// HTTP Body
request.httpBody = try JSONEncoder().encode(post)
let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse, http.statusCode == 201 else {
throw NetworkError.serverError
}
return try JSONDecoder().decode(Post.self, from: data)
}
3. URLComponents bilan Query Parametrlar
https://api.example.com/search?query=swift&page=1&limit=20 ko'rinishidagi so'rovlarni xavfsiz shakllantirish:
var components = URLComponents(string: "https://api.example.com/search")
components?.queryItems = [
URLQueryItem(name: "query", value: "swift"),
URLQueryItem(name: "page", value: "1"),
URLQueryItem(name: "limit", value: "20")
]
guard let url = components?.url else { return }
4. Codable va CodingKeys
Serverdagi snake_case kalitlarini Swift'dagi camelCase xususiyatlariga o'tkazish.
struct UserProfile: Codable {
let id: Int
let fullName: String
let avatarURL: URL?
let createdAt: Date
enum CodingKeys: String, CustomCodingKey, CodingKey {
case id
case fullName = "full_name"
case avatarURL = "avatar_url"
case createdAt = "created_at"
}
}
JSONDecoder Sozlamalari:
Agar barcha kalitlar snake_case bo'lsa, CodingKeys yozmasdan avtomatik konvertatsiya qilish mumkin:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601 // yoki .secondsSince1970
let profile = try decoder.decode(UserProfile.self, from: jsonData)
5. Custom Decoding (init(from:))
Kutilmagan JSON tuzilmalarini yoki ichma-ich (nested) ma'lumotlarni tekislash (flattening) uchun:
/*
JSON ko'rinishi:
{
"id": 1,
"user": {
"details": {
"city": "Toshkent"
}
}
}
*/
struct CityInfo: Decodable {
let id: Int
let city: String
enum RootKeys: String, CodingKey { case id, user }
enum UserKeys: String, CodingKey { case details }
enum DetailsKeys: String, CodingKey { case city }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
let userContainer = try container.nestedContainer(keyedBy: UserKeys.self, forKey: .user)
let detailsContainer = try userContainer.nestedContainer(keyedBy: DetailsKeys.self, forKey: .details)
self.city = try detailsContainer.decode(String.self, forKey: .city)
}
}
6. Xatoliklarni (Error Handling) To'g'ri Tashkil Qilish
enum APIError: Error, LocalizedError {
case invalidURL
case badStatusCode(Int)
case decodingFailed(Error)
case noInternet
case unknown(Error)
var errorDescription: String? {
switch self {
case .invalidURL: return "Noto'g'ri URL manzil"
case .badStatusCode(let code): return "Server xatosi: HTTP \(code)"
case .decodingFailed: return "Ma'lumotni o'qib bo'lmadi"
case .noInternet: return "Internet bilan aloqa yo'q"
case .unknown(let error): return error.localizedDescription
}
}
}
7. Generic API Client Namunasi
Loyihalarda qayta ishlatiladigan universal tarmoq mijozi:
protocol NetworkServiceProtocol {
func request<T: Decodable>(endpoint: Endpoint) async throws -> T
}
final class NetworkClient: NetworkServiceProtocol {
static let shared = NetworkClient()
private let session: URLSession
init(session: URLSession = .shared) {
self.session = session
}
func request<T: Decodable>(endpoint: Endpoint) async throws -> T {
guard let url = endpoint.url else { throw APIError.invalidURL }
var request = URLRequest(url: url)
request.httpMethod = endpoint.method.rawValue
request.allHTTPHeaderFields = endpoint.headers
request.httpBody = endpoint.body
let data: Data
let response: URLResponse
do {
(data, response) = try await session.data(for: request)
} catch let urlError as URLError where urlError.code == .notConnectedToInternet {
throw APIError.noInternet
} catch {
throw APIError.unknown(error)
}
guard let http = response as? HTTPURLResponse else {
throw APIError.invalidURL
}
guard (200...299).contains(http.statusCode) else {
throw APIError.badStatusCode(http.statusCode)
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return try decoder.decode(T.self, from: data)
} catch {
throw APIError.decodingFailed(error)
}
}
}