- Published on
Swift-da URLSession va Escaping Closure bilan API-dan JSON yuklab olish
- Authors
- Name
- ShoxruxC
- @iOSdasturchi
Oldingi videolarda background thread, escaping closure va Codable protokoli o'rganildi. Bu videoda ularning barchasi birlashadi — haqiqiy API-dan ma'lumot yuklab, ilovada ekranda ko'rsatiladi.
Test uchun bepul ochiq API ishlatiladi: jsonplaceholder.typicode.com — bu haqiqiy backend bo'lmasdan ham JSON ma'lumot qaytaruvchi xizmat.
Model
API-dan kelayotgan JSON formatiga qarab model yaratiladi:
// API dan keladigan JSON:
// { "userId": 1, "id": 1, "title": "...", "body": "..." }
struct PostModel: Identifiable, Codable {
let userId: Int
let id: Int
let title: String
let body: String
}
Identifiable — ForEach-da ishlatish uchun. Codable — JSON-dan avtomatik decode qilish uchun. Xususiyat nomlari JSON kalit nomlari bilan aynan mos bo'lishi shart — aks holda decode ishlaydi, lekin qiymatlar nil bo'ladi.
ViewModel — asosiy tuzilma
class DownloadWithEscapingViewModel: ObservableObject {
@Published var posts: [PostModel] = []
init() {
getPosts()
}
func getPosts() { }
}
URLSession bilan ma'lumot yuklash
func getPosts() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") else {
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
// 1. Data borligini tekshirish
guard let data = data else {
print("Ma'lumot yo'q")
return
}
// 2. Xato yo'qligini tekshirish
guard error == nil else {
print("Xato: \(error!)")
return
}
// 3. Response HTTP URL Response ekanini tekshirish
guard let response = response as? HTTPURLResponse else {
print("Noto'g'ri response")
return
}
// 4. Status code 200-299 orasida ekanini tekshirish
guard response.statusCode >= 200 && response.statusCode < 300 else {
print("Status code noto'g'ri: \(response.statusCode)")
return
}
// Barcha tekshiruvlar o'tdi — ma'lumot tayyor
print("Muvaffaqiyatli yuklandi")
}.resume() // ← .resume() bo'lmasa task BOSHLANMAYDI
}
.resume() nima?
dataTask task-ni tayyorlaydi, lekin ishga tushirmaydi. .resume() — ishga tushirish buyrug'i. Nomi "resume" — chunki pause/suspend qilingan task-ni ham qayta ishga tushirish uchun ishlatiladi.
HTTP status code-lar
200–299 — Muvaffaqiyatli
400–499 — Client xatosi (noto'g'ri so'rov)
500–599 — Server xatosi
Keng tarqalgan qisqartma — bitta guard
Yuqoridagi to'rtta tekshiruvni bitta guard-ga birlashtirish mumkin:
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let data = data,
error == nil,
let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300
else {
print("Ma'lumot yuklanmadi")
return
}
// Shu yerda data tayyor
}.resume()
Decode va UI yangilash
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
guard let data = data, error == nil else { return }
// Codable bilan decode
guard let newPost = try? JSONDecoder().decode(PostModel.self, from: data) else {
return
}
// UI yangilash — MAIN THREAD-DA bo'lishi shart
DispatchQueue.main.async {
self?.posts.append(newPost)
}
}.resume()
Nima uchun DispatchQueue.main kerak?
URLSession.dataTask avtomatik ravishda background thread-ga o'tadi. Ma'lumot yuklanib bo'lganda ham background thread-da turiladi. @Published o'zgaruvchini background thread-dan yangilash — Xcode xatosi beradi:
Publishing changes from background threads is not allowed
Shuning uchun DispatchQueue.main.async { } ichida yangilash — bu asosiy qoidalardan biri.
weak self nima uchun?
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
DispatchQueue.main.async {
self?.posts.append(newPost) // weak bo'lgani uchun optional
}
}
[weak self] — strong reference cycle (retain cycle) oldini olish uchun. Agar weak bo'lmasa, closure ViewModel-ni "ushlab" turadi — xotiradan chiqarilmaydi. weak bilan — ViewModel yo'q bo'lsa, self nil bo'ladi va xotira muammosi bo'lmaydi.
Umumiy downloadData funksiyasi
Bir nechta turli URL-lardan ma'lumot yuklash kerak bo'lganda, har safar yuqoridagi kodni qayta yozish keraksiz. Umumiy funksiya:
func downloadData(fromURL url: URL, completion: @escaping (_ returnedData: Data?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let data = data,
error == nil,
let response = response as? HTTPURLResponse,
response.statusCode >= 200 && response.statusCode < 300
else {
completion(nil) // muvaffaqiyatsiz — nil qaytarish
return
}
completion(data) // muvaffaqiyatli — data qaytarish
}.resume()
}
@escaping — async funksiya bo'lgani uchun: downloadData tugashidan keyin completion chaqiriladi (escaping closure mavzusida tushuntirilgan).
Foydalanish
func getPosts() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1") else {
return
}
downloadData(fromURL: url) { [weak self] returnedData in
if let data = returnedData {
guard let newPost = try? JSONDecoder().decode(PostModel.self, from: data) else {
return
}
DispatchQueue.main.async {
self?.posts.append(newPost)
}
}
}
}
Massiv postlarni yuklash
Bitta post o'rniga barcha postlarni yuklash:
func getPosts() {
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else {
return
}
downloadData(fromURL: url) { [weak self] returnedData in
if let data = returnedData {
// [PostModel] — massiv, PostModel emas
guard let newPosts = try? JSONDecoder().decode([PostModel].self, from: data) else {
return
}
DispatchQueue.main.async {
self?.posts = newPosts // append emas, to'liq almashtirish
}
}
}
}
View
struct DownloadWithEscapingBootcamp: View {
@StateObject var vm = DownloadWithEscapingViewModel()
var body: some View {
List {
ForEach(vm.posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.foregroundColor(.gray)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
}
app.quicktype.io — JSON dan Model yaratish vositasi
Murakkab JSON-da model yaratish qiyin bo'lishi mumkin — xususiyatlar ko'p, turlar har xil. app.quicktype.io vositasida JSON ma'lumotni joylashtirish, til sifatida Swift-ni tanlash — va tayyor Codable struct avtomatik yaratiladi. Bu boshlang'ich va tajribali dasturchilarga ham vaqt tejaydi.
Xulosa
URL → URLSession.dataTask → guard (data, error, response, statusCode)
→ JSONDecoder().decode → DispatchQueue.main.async → @Published o'zgaruvchi
| Tushuncha | Ma'nosi |
|---|---|
.resume() | Task-ni ishga tushirish — unutilsa task boshlanmaydi |
| Status code 200–299 | Muvaffaqiyatli response |
DispatchQueue.main.async | UI yangilash asosiy thread-da bo'lishi shart |
[weak self] | Strong reference cycle-dan himoya |
@escaping | Async closure — funksiya tugagandan keyin chaqiriladi |
Keyingi videoda xuddi shu narsa Combine framework yordamida yanada qulay tarzda amalga oshiriladi.