Azimjon Abdurasulov

SwiftUI 7: SwiftUI ichki arxitekturasi — View identifikatsiyasi, Observation va UIKit bilan aloqa

📱 UIKit va SwiftUI — chuqur

SwiftUI 7: SwiftUI ichki arxitekturasi — View identifikatsiyasi, Observation va UIKit bilan aloqa

Holat: 2026-yil sentabr. SwiftUI oddiy deklarativ sintaksisdek ko'rinsa-da, uning kapoti ostida murakkab graf nazariyasi va identifikatsiya algoritmlari yotadi. Nega ba'zida @State o'z-o'zidan nolga tushib qoladi? Nega view keraksiz qayta chiziladi? Ushbu darsda Strukturaviy va Aniq identifikatsiya, Observation (@Observable) ning xotira darajasidagi ishlashi hamda UIKit bilan ikki tomonlama integratsiya ko'rib chiqiladi.

Mundarija

  1. View identifikatsiyasi: Strukturaviy vs Aniq (Explicit)
  2. Identifikatorning xotira hayot davriga ta'siri
  3. Observation Framework (@Observable) evolyutsiyasi
  4. body qachon qayta ishga tushadi: _printChanges()
  5. UIKit'ni SwiftUI'ga olib kirish: UIViewRepresentable
  6. SwiftUI'ni UIKit'ga olib kirish: UIHostingController
  7. Zamonaviy kataklar: UIHostingConfiguration
  8. Xulosa
  9. Manbalar

1. View identifikatsiyasi: Strukturaviy vs Aniq (Explicit) {#identity}

SwiftUI'da view struktura bo'lgani sababli, uning xotira manzili (pointer) doimiy emas. Tizim qaysi view o'zgarganini bilish uchun ikki xil identifikatsiyadan foydalanadi:

1. Strukturaviy identifikatsiya (Structural Identity)

View daraxtidagi ierarxik o'rni orqali aniqlanadi. Masalan:

import SwiftUI

struct ConditionalView: View {
  var isFlag: Bool

  var body: some View {
    if isFlag {
      CounterView() // Shox A (Identifikator: ConditionalView.True)
    } else {
      CounterView() // Shox B (Identifikator: ConditionalView.False)
    }
  }
}

struct CounterView: View {
  @State private var count = 0
  var body: some View {
    Button("Bosildi: \(count)") { count += 1 }
  }
}

Tuzoq: Garchi ikkala shoxda ham CounterView() yozilgan bo'lsa-da, isFlag o'zgarganda SwiftUI eski shoxdagi view'ni butunlay o'chiradi va yangi shoxdagisini yaratadi — natijada @State var count 0 ga aylanib qoladi!

2. Aniq identifikatsiya (Explicit Identity)

Dasturchi tomonidan .id(...) yoki ForEach(items, id: \.id) orqali beriladigan qat'iy identifikator.


2. Identifikatorning xotira hayot davriga ta'siri {#identity-lifetime}

Agar siz biror view'ning holatini majburan noldan tozalab, animatsiya bilan qayta ishga tushirmoqchi bo'lsangiz, uning .id(...) sini o'zgartirish kifoya:

import SwiftUI

struct ProfileResetContainer: View {
  @State private var sessionID = UUID()

  var body: some View {
    VStack {
      ProfileDetailsView()
        .id(sessionID) // sessionID o'zgarsa, ichki barcha State tozalanadi

      Button("Sessiyani yangilash") {
        sessionID = UUID()
      }
    }
  }
}

struct ProfileDetailsView: View {
  @State private var text = "Qoralama"
  var body: some View {
    TextField("Matn", text: $text)
  }
}

3. Observation Framework (@Observable) evolyutsiyasi {#observation}

iOS 17 dan boshlab (va iOS 26/27 da to'liq standart) Swift Syntax Makroslariga asoslangan Observation Framework kiritildi:

  • **Eski usul (ObservableObject + @Published):** Ob'ekt ichidagi istalgan bitta @Published maydon o'zgarsa ham, butun view to'liq qayta hisoblanar edi.
  • **Zamonaviy usul (@Observable): Tizim maydon darajasida kuzatuv (Property-level tracking**) olib boradi.
import SwiftUI

@Observable
public final class UserProfileModel {
  public var name: String = "Azimjon"
  public var age: Int = 26
  public var lastLogin: Date = Date()
}

public struct UserProfileView: View {
  @Bindable public var model: UserProfileModel

  public var body: some View {
    VStack(spacing: 8) {
      // Faqat name o'qilmoqda:
      Text(model.name)
      TextField("Ism", text: $model.name)
    }
  }
}

Agar fonda model.age yoki model.lastLogin qiymati o'zgarsa, UserProfileView qayta chizilmaydi, chunki uning bodysi faqat name ga bog'langan! Bu xotira va CPU sarfini keskin tejaydi.


4. body qachon qayta ishga tushadi: _printChanges() {#body-evaluation}

SwiftUI'da body quyidagi 4 holatda qayta hisoblanadi:

  1. View'ning o'z @State o'zgaruvchisi o'zgarganda;
  2. body ichida o'qilgan @Observable modeli maydoni o'zgarganda;
  3. @Environment qiymati (masalan, rang mavzusi) yangilanganda;
  4. Ota ko'rinishdan uzatilgan tashqi parametr o'zgarganda.

Nosozliklarni qidirish (Debugging):

Qaysi sababga ko'ra ko'rinish qayta chizilayotganini bilish uchun maxsus yashirin metoddan foydalaniladi:

import SwiftUI

struct DebuggingView: View {
  @State private var count = 0

  var body: some View {
    #if DEBUG
    let _ = Self._printChanges()
    #endif

    Button("Soni: \(count)") {
      count += 1
    }
  }
}

Xcode konsolida: DebuggingView: @self, _count changed. degan aniq diagnostika chiqadi.


5. UIKit'ni SwiftUI'ga olib kirish: UIViewRepresentable {#uiview-representable}

SwiftUI'da hali mavjud bo'lmagan yoki murakkab UIKit komponentlarini (masalan, maxsus datchiklar, WebView yoki kamera ko'rinishi) SwiftUI'ga o'rash:

import SwiftUI
import UIKit

public struct NativeSpinner: UIViewRepresentable {
  public var isSpinning: Bool

  public func makeUIView(context: Context) -> UIActivityIndicatorView {
    UIActivityIndicatorView(style: .medium)
  }

  public func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
    if isSpinning {
      uiView.startAnimating()
    } else {
      uiView.stopAnimating()
    }
  }
}

6. SwiftUI'ni UIKit'ga olib kirish: UIHostingController {#hosting-controller}

Mavjud UIKit arxitekturasidagi ekran ichiga yangi SwiftUI modulini joylashtirish:

import SwiftUI
import UIKit

@MainActor
func embedSwiftUIView(in parentVC: UIViewController) {
  let swiftUIView = Text("Salom SwiftUI!")
  let hostingController = UIHostingController(rootView: swiftUIView)

  parentVC.addChild(hostingController)
  hostingController.view.frame = CGRect(x: 20, y: 100, width: 300, height: 100)
  parentVC.view.addSubview(hostingController.view)
  hostingController.didMove(toParent: parentVC)
}

7. Zamonaviy kataklar: UIHostingConfiguration {#hosting-configuration}

iOS 16+ dan boshlab UITableViewCell yoki UICollectionViewCell ichida maxsus subview yaratish shart emas — to'g'ridan-to'g'ri **UIHostingConfiguration** orqali SwiftUI ko'rinishini katakka o'rnatish mumkin:

import SwiftUI
import UIKit

@MainActor
func configureCustomCell(cell: UITableViewCell, title: String) {
  cell.contentConfiguration = UIHostingConfiguration {
    HStack {
      Image(systemName: "checkmark.seal.fill")
        .foregroundStyle(.blue)
      Text(title)
        .font(.body)
      Spacer()
    }
    .padding(.horizontal, 16)
  }
}

8. Xulosa {#xulosa}

  • Strukturaviy identifikatsiya view daraxtidagi shoxlarga bog'liq; shartli bloklarda (if-else) holat yo'qolib ketmasligi uchun ehtiyot bo'lish lozim.
  • .id(...) identifikatori o'zgarganda SwiftUI ob'ektni xotiradan to'liq tozalab, qayta tiklaydi.
  • @Observable makrosi faqat body ichida real o'qilgan maydonlarni kuzatadi va unumdorlikni keskin oshiradi.
  • Self._printChanges() orqali keraksiz qayta chizilish sabablarini tezkor aniqlash mumkin.
  • UIHostingConfiguration UIKit ro'yxatlarida SwiftUI afzalliklaridan foydalanishning eng zamonaviy usulidir.

Manbalar {#manbalar}

  1. Demystify SwiftUI — WWDC21
  2. Discover Observation in SwiftUI — WWDC23
  3. UIViewRepresentable — Apple Documentation
  4. UIHostingConfiguration — Apple Documentation

Markdown manba

iOS Lessons