Codablestrukturları ilə JSON-u oxumaq və yazmaqURLSessionvəasync/awaitilə şəbəkə sorğusu yazmaq və xətaları emal etmək- Yükləməni
.taskvə.refreshableilə ekrana bağlamaq - SwiftData-da
@Model,@QueryvəmodelContextilə işləmək
Elvin təyyarədə Educora-nı açır. İnternet yoxdur, amma dünən yazdığı qeydlər yerindədir, yerə enəndə isə tətbiq yeni dərsləri serverdən sakitcə yükləyir. Bunun üçün iki bacarıq lazımdır: şəbəkədən məlumat almaq (JSON, URLSession, async/await) və onu cihazda saxlamaq (SwiftData). Bu dərsdə hər ikisini addım-addım quracağıq.
Codable: JSON-dan Swift-ə və geri
Serverlər məlumatı adətən JSON mətni kimi göndərir. Strukturu Codable protokoluna uyğun elan etsən, Swift onu JSON-dan oxumağı (JSONDecoder) və JSON-a yazmağı (JSONEncoder) özü öyrənir — bir sətir də əlavə kod lazım deyil. Xassələrin adları JSON-dakı açarlarla eyni olmalıdır; JSON-da artıq açarlar varsa, onlar sadəcə buraxılır.
import Foundation
struct Lesson: Codable {
let id: Int
let title: String
let minutes: Int
}
let json = """
[{"id": 1, "title": "Variables", "minutes": 10},
{"id": 2, "title": "Loops", "minutes": 15, "level": "beginner"}]
"""
let lessons = try JSONDecoder().decode([Lesson].self, from: Data(json.utf8))
for lesson in lessons {
print("\(lesson.id). \(lesson.title) - \(lesson.minutes) min")
}
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
let data = try encoder.encode(lessons[0])
print(String(decoding: data, as: UTF8.self))1. Variables - 10 min
2. Loops - 15 min
{"id":1,"minutes":10,"title":"Variables"}level açarı strukturda olmadığı üçün buraxılır; .sortedKeys açarları əlifba sırası ilə yazır.URLSession və async/await
URLSession.shared.data(from:) async funksiyadır: await sözü onun cavabı gözlədiyi yeri göstərir, amma əsas axın bu vaxt bloklanmır — ekran cavab verməyə davam edir. Funksiya (Data, URLResponse) cütü qaytarır. Şəbəkə kəsiləndə o xəta atır, ona görə çağırışın qarşısında try da yazılır. Nümunədə sınaq üçün açıq olan JSONPlaceholder xidmətindən tapşırıqlar yüklənir.
struct Todo: Codable, Identifiable {
let id: Int
let title: String
let completed: Bool
}
enum APIError: Error {
case badStatus(Int)
}
func fetchTodos() async throws -> [Todo] {
let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
let (data, response) = try await URLSession.shared.data(from: url)
if let http = response as? HTTPURLResponse, !(200...299).contains(http.statusCode) {
throw APIError.badStatus(http.statusCode)
}
return try JSONDecoder().decode([Todo].self, from: data)
}decode xəta atacaq.func fetchTodos(completion: @escaping (Result<[Todo], Error>) -> Void) {
let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
URLSession.shared.dataTask(with: url) { data, _, error in
if let error {
completion(.failure(error))
return
}
do {
let todos = try JSONDecoder().decode([Todo].self, from: data ?? Data())
completion(.success(todos))
} catch {
completion(.failure(error))
}
}.resume()
}func fetchTodos() async throws -> [Todo] {
let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([Todo].self, from: data)
}.resume() unudulsa, sorğu heç vaxt göndərilməz, bir completion çağırışını buraxmaq isə ekranı əbədi «yüklənir» vəziyyətində saxlayar. Sağda kod yuxarıdan aşağı oxunur, xətalar throws ilə özü yuxarı ötürülür.@MainActor
@Observable
final class TodosModel {
var todos: [Todo] = []
var isLoading = false
var errorMessage: String?
func load() async {
isLoading = true
defer { isLoading = false }
do {
todos = try await fetchTodos()
errorMessage = nil
} catch {
errorMessage = "Could not load the list"
}
}
}@MainActor modelin xassələrinin yalnız əsas axında dəyişməsinə zəmanət verir — interfeys məhz orada yenilənir.struct TodosView: View {
@State private var model = TodosModel()
var body: some View {
List(model.todos) { todo in
Label(todo.title, systemImage: todo.completed ? "checkmark.circle.fill" : "circle")
}
.overlay {
if model.isLoading { ProgressView() }
}
.task { await model.load() }
.refreshable { await model.load() }
}
}defer bloku funksiyadan necə çıxılmasından asılı olmayaraq — uğurla və ya xəta ilə — sonda icra olunur, ona görə indikator heç vaxt ekranda «ilişib» qalmır. errorMessage dolanda siyahının yerinə iOS 17-dəki hazır boş vəziyyət görünüşünü göstərmək rahatdır: ContentUnavailableView("No connection", systemImage: "wifi.slash"). Onun altına «Try again» düyməsi qoyub yenidən load() çağırmaq olar.
SwiftData: cihazda saxlamaq
SwiftData (iOS 17+) Swift sinfini verilənlər bazasında saxlamağın ən qısa yoludur. Sinfin qarşısına @Model yazırsan, tətbiqdə .modelContainer(for:) ilə anbar yaradırsan, görünüşdə isə @Query ilə məlumatı oxuyursan. Yeni obyekt modelContext.insert(...) ilə əlavə olunur, delete ilə silinir; dəyişikliklər avtomatik yadda saxlanır, @Query isə siyahını özü yeniləyir.
import SwiftData
import SwiftUI
@Model
final class Note {
var text: String
var createdAt: Date
init(text: String, createdAt: Date = .now) {
self.text = text
self.createdAt = createdAt
}
}
@main
struct EducoraNotesApp: App {
var body: some Scene {
WindowGroup {
NotesView()
}
.modelContainer(for: Note.self)
}
}struct NotesView: View {
@Environment(\.modelContext) private var context
@Query(sort: \Note.createdAt, order: .reverse) private var notes: [Note]
var body: some View {
NavigationStack {
List {
ForEach(notes) { note in
Text(note.text)
}
.onDelete { offsets in
for index in offsets { context.delete(notes[index]) }
}
}
.navigationTitle("Notes")
.toolbar {
Button("Add", systemImage: "plus") {
context.insert(Note(text: "New note"))
}
}
}
}
}+ yeni qeyd əlavə edir, sola sürüşdürmək silir. Tətbiqi bağlayıb açanda qeydlər yerində qalır.| Nə saxlanır | Harada |
|---|---|
| kiçik parametrlər: tema, gündəlik məqsəd | @AppStorage (UserDefaults) |
| qeydlər, nəticələr, axtarılan siyahılar | SwiftData |
| parollar və giriş tokenləri | Keychain |
| bütün cihazlarda eyni olmalı olan irəliləyiş | server |
@AppStorage("dailyGoal") private var dailyGoal = 15 qiyməti tətbiq bağlanandan sonra da saxlayır.- 1SwiftData ilə layihə
Yeni layihə yaradarkən
StoragesahəsindəSwiftDataseç — Xcode nümunəItemmodeli,.modelContainervə siyahı ekranını özü yaradacaq. - 2Önizləmə üçün yaddaşda anbar
Önizləmə real bazanı korlamasın deyə yaz:
#Preview { NotesView().modelContainer(for: Note.self, inMemory: true) }. - 3Sazlayıcı ilə addım-addım irəlilə
load()funksiyasının içində dayanma nöqtəsi qoy, tətbiqi işə sal vəF6ilə sətir-sətir irəlilə:todosmassivinin necə dolduğunu görəcəksən.
Əsas fikirlər
Codablestruktur JSON-danJSONDecoderilə oxunur vəJSONEncoderilə yazılır; artıq açarlar buraxılır.try await URLSession.shared.data(from:)(Data, URLResponse)qaytarır və ekranı bloklamır.- Model
@MainActor @Observableolur;.taskyükləməni başladır və özü ləğv edir,.refreshableyeniləmə jesti verir. - SwiftData:
@Modelsinif, tətbiqdə.modelContainer(for:), görünüşdə@QueryvəmodelContext.insert/delete. - Kiçik parametrlər
@AppStorage-də, parollar Keychain-də saxlanır; yalnız HTTPS-dən istifadə et.
Özünü yoxla
10 sual. Hər düzgün cavab XP qazandırır.
try await URLSession.shared.data(from: url) nə qaytarır?