شبکه سازی (Networking)
در «شبکه سازی SwiftUI» با URLSession داده می گیریم و می فرستیم. سپس نتیجه را در View نشان می دهیم. بنابراین برنامه زنده و آنلاین می شود. برای شروع، بخش شبکه سازی SwiftUI را اینجا می بینی.
درخواست GET با async/await
درخواست «GET» یعنی فقط خواندن داده از سرور. «async/await» الگوی ساده کارِ هم زمانی است. سپس جواب را با JSONDecoder تبدیل می کنیم.
import SwiftUI
import Foundation
struct Todo: Decodable, Identifiable {
let id: Int
let title: String
}
func fetchTodos() async throws -> [Todo] {
let url = URL(string: "https://jsonplaceholder.typicode.com/todos?_limit=2")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([Todo].self, from: data)
}
struct NetworkingGetDemo: View {
@State private var todos: [Todo] = []
var body: some View {
List(todos) { t in
Text(t.title)
}
.task {
do {
todos = try await fetchTodos()
} catch {
print(error)
}
}
}
}
import SwiftUI
struct ContentView: View {
var body: some View {
NetworkingGetDemo()
}
}
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
گام های عملی GET
- یک URL معتبر بساز.
- با data(from:) داده بگیر.
- JSON را با JSONDecoder تبدیل کن.
- لیست را در View نمایش بده.
نکته: همیشه خطا را مدیریت کن. بنابراین کرش نمی کنی.
الگوی ViewModel برای شبکه سازی
«ViewModel» منطق داده را جدا نگه می دارد. بنابراین View ساده می ماند. سپس فقط نمایش می دهد.
import SwiftUI
import Foundation
struct NoteDTO: Decodable, Identifiable {
let id: Int
let title: String
let body: String
}
enum API {
static func fetchNotes() async throws -> [NoteDTO] {
let url = URL(string: "https://jsonplaceholder.typicode.com/posts?_limit=3")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([NoteDTO].self, from: data)
}
}
@MainActor
final class NotesViewModel: ObservableObject {
@Published var notes: [NoteDTO] = []
func loadFromAPI() async {
do {
notes = try await API.fetchNotes()
} catch {
print(error)
}
}
}
struct NotesView: View {
@StateObject private var vm = NotesViewModel()
var body: some View {
List(vm.notes) { n in
Text(n.title)
}
.task {
await vm.loadFromAPI()
}
}
}
import SwiftUI
struct ContentView: View {
var body: some View {
NotesView()
}
}
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
درخواست POST و ارسال JSON
«POST» یعنی فرستادن داده به سرور. بنابراین باید بدنه JSON بسازیم. سپس هدر مناسب تنظیم کنیم.
import SwiftUI
import Foundation
struct NewTodo: Encodable {
let title: String
let completed: Bool
}
func createTodo(_ todo: NewTodo) async throws -> Int {
let url = URL(string: "https://jsonplaceholder.typicode.com/todos")!
var req = URLRequest(url: url)
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = try JSONEncoder().encode(todo)
let (_, resp) = try await URLSession.shared.data(for: req)
return (resp as? HTTPURLResponse)?.statusCode ?? 0
}
struct NetworkingPostDemo: View {
@State private var status: Int? = nil
var body: some View {
VStack(spacing: 12) {
Button("Create Todo") {
Task {
do {
status = try await createTodo(NewTodo(title: "Demo", completed: false))
} catch {
print(error)
status = nil
}
}
}
if let status {
Text("Status: \(status)")
}
}
.padding()
}
}
import SwiftUI
struct ContentView: View {
var body: some View {
NetworkingPostDemo()
}
}
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
هشدار: فقط در صورت نیاز، استثنائات ATS را در Info.plist تنظیم کن. بنابراین امنیت حفظ می شود.
جمع بندی سریع
- GET برای خواندن داده است.
- POST برای ارسال داده است.
- async/await کدنویسی را ساده می کند.
- ViewModel منطق شبکه را جدا می کند.