12. Swift Package Manager (SPM) Modulli Arxitektura Masterclass
Arxitektura
Swift Package Manager (SPM) & Modulli Arxitektura Masterclass
Holat: 2026-yil, Swift 6.0, Xcode 16/27. Ushbu qo'llanma zamonaviy Apple ekotizimida monolit loyihalarni yuqori unumdorlikka ega modulli SPM arxitekturasiga o'tkazish, kompilyatsiya vaqtini (Build Performance) 10x–30x tezlashtirish, Swift 6 Strict Concurrency va
packageaccess level imkoniyatlarini to'liq ishga solish bo'yicha dunyo miqyosidagi (Apple, Airbnb, Uber, Point-Free) eng ilg'or amaliyotlarni qamrab oladi.
1. Kirish: Nega SPM Zamonaviy iOS Loyihalarining Yagona Standartiga Aylandi?
iOS dasturlash tarixida qaramliklarni boshqarish (dependency management) 3 ta katta davrni bosib o'tdi:
- Manual / Git Submodules davri (2008–2011): Har bir
.frameworkyoki.abinar fayli loyihaga qo'lda tortib kelingan, versiya to'qnashuvlari va header search paths azob bergan. - CocoaPods & Carthage davri (2011–2020): Ruby asosidagi CocoaPods uzoq yillar hukmronlik qildi, biroq
.xcworkspaceyaratib.xcodeprojfayliga yashirin o'zgartirishlar kiritishi, git merge conflictlar va podfile lock muammolari jamoaviy ishlashni sekinlashtirdi. - Swift Package Manager (SPM) Davri (2020–Hozir): Apple tomonidan rasmiy taqdim etilgan, Swift kompilyatoriga to'g'ridan-to'g'ri integratsiya qilingan, deklarativ
Package.swiftmanifestiga ega birinchi darajali (first-class citizen) tizim.
<div style="margin: 1.5rem 0; overflow-x: auto;"> <svg viewBox="0 0 820 160" width="100%" style="background: rgba(15, 23, 42, 0.7); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"> <defs> <linearGradient id="grad1" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#ef4444" stop-opacity="0.25"/> <stop offset="100%" stop-color="#b91c1c" stop-opacity="0.1"/> </linearGradient> <linearGradient id="grad2" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#f59e0b" stop-opacity="0.25"/> <stop offset="100%" stop-color="#b45309" stop-opacity="0.1"/> </linearGradient> <linearGradient id="grad3" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#22c55e" stop-opacity="0.3"/> <stop offset="100%" stop-color="#15803d" stop-opacity="0.15"/> </linearGradient> <marker id="arrow" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"> <path d="M 0 1 L 8 5 L 0 9 z" fill="#64748b"/> </marker> </defs> <g transform="translate(20, 20)"> <rect width="230" height="120" rx="10" fill="url(#grad1)" stroke="#ef4444" stroke-width="1.5"/> <text x="16" y="32" fill="#f87171" font-size="11" font-weight="700" letter-spacing="1">2011 — 2020 DAVRI</text> <text x="16" y="56" fill="#ffffff" font-size="16" font-weight="700">CocoaPods</text> <text x="16" y="80" fill="#cbd5e1" font-size="12">• Ruby & Podfile bog'liqligi</text> <text x="16" y="98" fill="#cbd5e1" font-size="12">• .xcodeproj buzilishi & ziddiyat</text> <text x="16" y="116" fill="#fca5a5" font-size="11" font-weight="600">🔴 Og'ir script build phaselar</text> </g> <path d="M 260 80 L 290 80" stroke="#64748b" stroke-width="2" marker-end="url(#arrow)"/> <g transform="translate(300, 20)"> <rect width="230" height="120" rx="10" fill="url(#grad2)" stroke="#f59e0b" stroke-width="1.5"/> <text x="16" y="32" fill="#fbbf24" font-size="11" font-weight="700" letter-spacing="1">2015 — 2020 DAVRI</text> <text x="16" y="56" fill="#ffffff" font-size="16" font-weight="700">Carthage</text> <text x="16" y="80" fill="#cbd5e1" font-size="12">• Desentralizatsiyalashgan</text> <text x="16" y="98" fill="#cbd5e1" font-size="12">• Qo'lda framework qo'shish</text> <text x="16" y="116" fill="#fde68a" font-size="11" font-weight="600">🟡 XCFramework murakkabligi</text> </g> <path d="M 540 80 L 570 80" stroke="#64748b" stroke-width="2" marker-end="url(#arrow)"/> <g transform="translate(580, 20)"> <rect width="220" height="120" rx="10" fill="url(#grad3)" stroke="#22c55e" stroke-width="2"/> <text x="16" y="32" fill="#4ade80" font-size="11" font-weight="700" letter-spacing="1">2020 — HOZIR (STANDART)</text> <text x="16" y="56" fill="#ffffff" font-size="16" font-weight="700">Swift Package Manager</text> <text x="16" y="80" fill="#cbd5e1" font-size="12">• Swift kompilyatoriga nativ</text> <text x="16" y="98" fill="#cbd5e1" font-size="12">• Nol .xcodeproj buzilishi</text> <text x="16" y="116" fill="#86efac" font-size="11" font-weight="600">🟢 Swift 6 Concurrency & Plugins</text> </g> </svg> </div>
| Xususiyat | 🔴 CocoaPods (Eski Standart) | 🟢 Swift Package Manager (Zamonaviy Standart) |
|---|---|---|
| Integratsiya | Tashqi Ruby dependency, pod install buyrug'i | Swift Compiler nativ qismi, qo'shimcha toolsiz |
| Loyiha fayli | .xcodeproj va .xcworkspace ni o'zgartiradi (Merge conflictlar) | 0 ta .xcodeproj kodi, sof deklarativ Package.swift |
| Cross-Platform | Faqat Apple OS (iOS, macOS) | iOS, macOS, watchOS, visionOS, Linux, Windows, WASM |
| Build tizimi | Qo'shimcha og'ir Run Scripts build phaselar | To'g'ridan-to'g'ri Swift Build / llbuild |
| Strict Concurrency | Swift 6 Concurrency tekshiruvi qiyin | Swift 6 to'liq integratsiya (.enableUpcomingFeature) |
| Plugins & Macros | Cheklangan | Rasmiy BuildToolPlugin, CommandPlugin, Swift Macros |
Katta jamoalarda (10+ muhandis) monolit loyihani qismlarga bo'lmaslik quyidagi falokatlarga olib keladi:
- Build vaqtining haddan tashqari sekinligi: Bitta UI qatlamida o'zgartirilgan 1 qator kod butun ilovaning (barcha modellar va tarmoq qatlamlarining) qayta kompilyatsiyasiga sabab bo'ladi (Clean build 8–15 daqiqa, Incremental build 1.5–3 daqiqa).
- Merge Conflicts: Barcha muhandislar bitta ulkan
.xcodeprojXML fayliga o'z fayllarini qo'shadi, natijada har kuni o'nlab ziddiyatlar kelib chiqadi. - Arxitektura alg'ov-dalg'ovligi (Spaghetti Code): Chegara yo'qligi sababli, Login ekrani to'satdan Checkout ekrani modellariga to'g'ridan-to'g'ri murojaat qilaveradi.
SPM yechimi: Loyihani toza, yo'naltirilgan asiklik grafik (DAG — Directed Acyclic Graph) ko'rinishidagi modullarga ajratish.
1.1. Nima Uchun SPM Modul Qilinadi? (Project Level & Benchmark Tahlili)
Dasturchilar ko'pincha: "Mening loyihamga ham SPM modul kerakmi yoki monolitda qolganim ma'qulmi?" degan savolga duch kelishadi. Modullashtirish har doim ham bepul emas (ortiqcha boshlang'ich arxitektura vaqti talab qiladi). Shuning uchun loyiha darajasini to'g'ri baholash shart:
<div style="margin: 1.5rem 0; overflow-x: auto;"> <svg viewBox="0 0 820 280" width="100%" style="background: rgba(15, 23, 42, 0.7); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"> <defs> <linearGradient id="lvl3Grad" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#ef4444" stop-opacity="0.25"/> <stop offset="100%" stop-color="#b91c1c" stop-opacity="0.45"/> </linearGradient> <linearGradient id="lvl2Grad" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#f59e0b" stop-opacity="0.25"/> <stop offset="100%" stop-color="#d97706" stop-opacity="0.45"/> </linearGradient> <linearGradient id="lvl1Grad" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#10b981" stop-opacity="0.25"/> <stop offset="100%" stop-color="#059669" stop-opacity="0.45"/> </linearGradient> </defs> <text x="30" y="30" fill="#94a3b8" font-size="11" font-weight="700" letter-spacing="1">QAYSI LOYIHA DARAJASIDA SPM MODUL TANLANADI? (SCALE MATRIX)</text> <g transform="translate(30, 48)"> <rect width="760" height="65" rx="8" fill="url(#lvl3Grad)" stroke="#ef4444" stroke-width="1.8"/> <rect x="12" y="10" width="160" height="20" rx="4" fill="#ef4444"/> <text x="92" y="24" text-anchor="middle" fill="#ffffff" font-size="10" font-weight="800">LEVEL 3: ENTERPRISE & SUPERAPP</text> <text x="185" y="24" fill="#ffffff" font-size="14" font-weight="700">10–50+ muhandis • 40+ ekran • Bank, FinTech, E-Commerce</text> <text x="12" y="50" fill="#fca5a5" font-size="12">✅ <tspan font-weight="700">Majburiy:</tspan> To'liq SPM Monorepo (Micro-features, Interface/Impl, Preview Micro-Applar, Binary Targets). CI 15 daqiqadan 2 daqiqaga tushadi.</text> </g> <g transform="translate(30, 123)"> <rect width="760" height="65" rx="8" fill="url(#lvl2Grad)" stroke="#f59e0b" stroke-width="1.8"/> <rect x="12" y="10" width="160" height="20" rx="4" fill="#f59e0b"/> <text x="92" y="24" text-anchor="middle" fill="#000000" font-size="10" font-weight="800">LEVEL 2: O'RTA PRODUCTION</text> <text x="185" y="24" fill="#ffffff" font-size="14" font-weight="700">3–8 muhandis • 15–30 ekran • O'sib borayotgan ilova</text> <text x="12" y="50" fill="#fde68a" font-size="12">✅ <tspan font-weight="700">Tavsiya:</tspan> 4–6 ta SPM modul (CoreNetwork, DesignSystem, StorageKit, 2-3 ta asosiy Feature). Merge conflictlar 0 ga tushadi.</text> </g> <g transform="translate(30, 198)"> <rect width="760" height="65" rx="8" fill="url(#lvl1Grad)" stroke="#10b981" stroke-width="1.8"/> <rect x="12" y="10" width="160" height="20" rx="4" fill="#10b981"/> <text x="92" y="24" text-anchor="middle" fill="#ffffff" font-size="10" font-weight="800">LEVEL 1: SOLO & KICHIK ILOVA</text> <text x="185" y="24" fill="#ffffff" font-size="14" font-weight="700">1 dasturchi • 1–8 ekran • MVP yoki Portfolio loyiha</text> <text x="12" y="50" fill="#a7f3d0" font-size="12">💡 <tspan font-weight="700">Optimal:</tspan> Monolit arxitektura yetarli. SPM faqat tashqi qaramliklar (Alamofire, Nuke) yoki 1 ta LogicPackage uchun kerak.</text> </g> </svg> </div>
Real Unumdorlik Ko'rsatkichlari (Benchmarks)
Quyidagi grafik 35 ta ekranga ega haqiqiy tijoriy iOS loyihasining **Monolit .xcodeproj va SPM Modulli Arxitektura**dagi solishtirma natijalarini ko'rsatadi:
<div style="margin: 1.5rem 0; overflow-x: auto;"> <svg viewBox="0 0 820 250" width="100%" style="background: rgba(15, 23, 42, 0.7); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"> <g transform="translate(25, 20)"> <text x="0" y="15" fill="#e2e8f0" font-size="13" font-weight="700">1. Incremental Build (1 qator kod o'zgarganda):</text> <rect x="0" y="28" width="550" height="18" rx="4" fill="rgba(239, 68, 68, 0.25)" stroke="#ef4444" stroke-width="1"/> <text x="560" y="42" fill="#fca5a5" font-size="12" font-family="monospace">Monolit: 105 soniya (1m 45s)</text> <rect x="0" y="52" width="20" height="18" rx="4" fill="#22c55e"/> <text x="30" y="66" fill="#86efac" font-size="12" font-family="monospace" font-weight="700">SPM Modular: 3.8 soniya ⚡️ (27x tezroq!)</text> </g> <g transform="translate(25, 100)"> <text x="0" y="15" fill="#e2e8f0" font-size="13" font-weight="700">2. App Cold Start (dyld pre-main yuklash vaqti):</text> <rect x="0" y="28" width="480" height="18" rx="4" fill="rgba(239, 68, 68, 0.25)" stroke="#ef4444" stroke-width="1"/> <text x="490" y="42" fill="#fca5a5" font-size="12" font-family="monospace">Dynamic Frameworks: 1,450 ms (1.45s qora ekran)</text> <rect x="0" y="52" width="70" height="18" rx="4" fill="#22c55e"/> <text x="80" y="66" fill="#86efac" font-size="12" font-family="monospace" font-weight="700">Static SPM: 210 ms ⚡️ (7x tezroq ochilish!)</text> </g> <g transform="translate(25, 180)"> <text x="0" y="15" fill="#e2e8f0" font-size="13" font-weight="700">3. Unit Testlarni ishga tushirish tezligi:</text> <rect x="0" y="28" width="400" height="18" rx="4" fill="rgba(239, 68, 68, 0.25)" stroke="#ef4444" stroke-width="1"/> <text x="410" y="42" fill="#fca5a5" font-size="12" font-family="monospace">Simulyator bilan: 35.0 soniya</text> <rect x="0" y="52" width="8" height="18" rx="4" fill="#22c55e"/> <text x="18" y="66" fill="#86efac" font-size="12" font-family="monospace" font-weight="700">SPM Izolyatsiyalangan Test: 0.04 soniya ⚡️ (875x tezroq!)</text> </g> </svg> </div>
2. Swift Package Anatomiyasi va Zamonaviy Manifest (Swift 6.0)
Har qanday Swift Package uning ildizida (root) joylashgan Package.swift deklarativ manifesti orqali boshqariladi.
MyPackage/
├── Package.swift # Paket manifesti (Arxitektura rejasi)
├── Package.resolved # Lock-file (Aniq commit hash va versiyalar)
├── Sources/ # Kod targetlari
│ ├── OrderFeature/ # Implementation
│ │ ├── OrderView.swift
│ │ └── OrderViewModel.swift
│ ├── OrderFeatureInterface/ # Sof Protocollar & DTO Modellar
│ │ └── OrderServiceProtocol.swift
│ └── DesignSystem/ # Resurslar va Umumiy UI
│ ├── Resources/ # Assets.xcassets, Fonts, Strings
│ └── Components/
└── Tests/ # Izolyatsiyalangan Unit Testlar
└── OrderFeatureTests/
Swift 6.0 Production Manifest Namunalari
Keling, zamonaviy barcha talablarga javob beradigan Package.swift manifestini ko'rib chiqamiz:
// swift-tools-version: 6.0
// Yuqoridagi izoh majburiy: Swift 6.0 kompilyator sintaksisini ishga tushiradi
import PackageDescription
let package = Package(
name: "CoreDomain",
defaultLocalization: "en", // Resurslar va mahalliylashtirish uchun
platforms: [
.iOS(.v17),
.macOS(.v14)
],
products: [
// 1. Static kutubxona (Tavsiya etiladi - launch time optimallashtirilgan)
.library(
name: "OrderFeature",
targets: ["OrderFeature"]
),
// 2. Interfeys kutubxonasi (Boshqa modullar faqat shunga ulanadi)
.library(
name: "OrderFeatureInterface",
targets: ["OrderFeatureInterface"]
),
// 3. Umumiy dizayn tizimi
.library(
name: "DesignSystem",
targets: ["DesignSystem"]
)
],
dependencies: [
// Tashqi qaramliklar aniq versiya yoki major oraliq bilan
.package(url: "https://github.com/pointfreeco/swift-dependencies.git", from: "1.4.0"),
.package(url: "https://github.com/kean/Nuke.git", .upToNextMajor(from: "12.8.0"))
],
targets: [
// ── A. INTERFEYS TARGETI (Zero Dependencies) ──
.target(
name: "OrderFeatureInterface",
dependencies: [
.product(name: "Dependencies", package: "swift-dependencies")
],
swiftSettings: [
.enableUpcomingFeature("StrictConcurrency"),
.swiftLanguageMode(.v6)
]
),
// ── B. IMPLEMENTATSIYA TARGETI ──
.target(
name: "OrderFeature",
dependencies: [
"OrderFeatureInterface", // O'z interfeysiga ulanadi
"DesignSystem",
.product(name: "NukeUI", package: "Nuke")
],
resources: [
.process("Resources") // Assets.xcassets va lokalizatsiyani jamlash
],
swiftSettings: [
.enableUpcomingFeature("StrictConcurrency"),
.swiftLanguageMode(.v6)
]
),
// ── C. DESIGN SYSTEM TARGETI ──
.target(
name: "DesignSystem",
resources: [
.process("Resources")
]
),
// ── D. IZOLYATSIYALANGAN UNIT TEST ──
.testTarget(
name: "OrderFeatureTests",
dependencies: [
"OrderFeature"
]
)
]
)
3. Swift 6 va Yangi package Access Modifier (SE-0386)
Swift 5.9 ga qadar katta modulli paketlarda arxitekturaviy ziddiyat mavjud edi:
- **
internal:** Faqat bitta target ichida ko'rinadi. Agar paket ichidaOrderFeaturevaOrderInternalKitalohida target bo'lsa,internalfunksiyalarni ikkinchi target chaqira olmas edi. - **
public:** Barcha targetlar ko'radi, ammo butun ilovaga (tashqi mijozlarga) ham ochilib ketadi. Natijada nozik yordamchi kodlar tashqariga chiqib, inkapsulyatsiya buzilar edi.
**SE-0386 (package Access Modifier):** Swift 5.9 va Swift 6 da yangi package kalit so'zi taqdim etildi. Bu modifikator belgilangan deklaratsiyani aynan bitta SPM paketiga tegishli barcha targetlar uchun ochiq qiladi, ammo paketdan tashqaridagi kod (masalan, asosiy ilova yoki boshqa paketlar) uni umuman ko'rmaydi!
<div style="margin: 1.5rem 0; overflow-x: auto;"> <svg viewBox="0 0 820 370" width="100%" style="background: rgba(15, 23, 42, 0.7); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"> <defs> <linearGradient id="gOpen" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#a855f7" stop-opacity="0.3"/> <stop offset="100%" stop-color="#7c3aed" stop-opacity="0.5"/> </linearGradient> <linearGradient id="gPublic" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#06b6d4" stop-opacity="0.3"/> <stop offset="100%" stop-color="#0891b2" stop-opacity="0.5"/> </linearGradient> <linearGradient id="gPackage" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#22c55e" stop-opacity="0.35"/> <stop offset="100%" stop-color="#15803d" stop-opacity="0.6"/> </linearGradient> <linearGradient id="gInternal" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#eab308" stop-opacity="0.3"/> <stop offset="100%" stop-color="#ca8a04" stop-opacity="0.5"/> </linearGradient> <linearGradient id="gFileprivate" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#f97316" stop-opacity="0.3"/> <stop offset="100%" stop-color="#c2410c" stop-opacity="0.5"/> </linearGradient> <linearGradient id="gPrivate" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#ef4444" stop-opacity="0.3"/> <stop offset="100%" stop-color="#b91c1c" stop-opacity="0.5"/> </linearGradient> </defs> <text x="30" y="32" fill="#94a3b8" font-size="11" font-weight="700" letter-spacing="1">SWIFT 6 ACCESS CONTROL IERARXIYASI (SE-0386)</text> <text x="790" y="32" text-anchor="end" fill="#64748b" font-size="11" font-family="monospace">TOR —> KENG DOIRA</text> <g transform="translate(30, 48)"> <rect width="760" height="42" rx="7" fill="url(#gOpen)" stroke="#a855f7" stroke-width="1.5"/> <rect x="8" y="8" width="80" height="26" rx="5" fill="#a855f7"/> <text x="48" y="25" text-anchor="middle" fill="#ffffff" font-family="monospace" font-size="13" font-weight="700">open</text> <text x="105" y="26" fill="#e9d5ff" font-size="13" font-weight="600">Barcha tashqi modullar ko'radi VA boshqa paketda subclass/override qilish mumkin</text> </g> <g transform="translate(30, 98)"> <rect width="760" height="42" rx="7" fill="url(#gPublic)" stroke="#06b6d4" stroke-width="1.5"/> <rect x="8" y="8" width="80" height="26" rx="5" fill="#06b6d4"/> <text x="48" y="25" text-anchor="middle" fill="#ffffff" font-family="monospace" font-size="13" font-weight="700">public</text> <text x="105" y="26" fill="#cffafe" font-size="13" font-weight="600">Barcha tashqi modullar ko'radi, lekin subclass/override qilib BO'LMAYDI</text> </g> <g transform="translate(30, 148)"> <rect width="760" height="46" rx="7" fill="url(#gPackage)" stroke="#22c55e" stroke-width="2.5"/> <rect x="8" y="8" width="95" height="30" rx="5" fill="#22c55e"/> <text x="55" y="27" text-anchor="middle" fill="#000000" font-family="monospace" font-size="13" font-weight="800">package ⭐️</text> <text x="120" y="24" fill="#4ade80" font-size="13" font-weight="700">Swift 6 Yangiligi:</text> <text x="250" y="24" fill="#ffffff" font-size="13" font-weight="600">Shu SPM paketi ichidagi BARCHA targetlar ko'radi, tashqi ilovadan germetik yashirin!</text> <text x="120" y="39" fill="#86efac" font-size="11">Ko'p targetli paketlarda inkapsulyatsiyani saqlashning eng toza kaliti</text> </g> <g transform="translate(30, 202)"> <rect width="760" height="42" rx="7" fill="url(#gInternal)" stroke="#eab308" stroke-width="1.5"/> <rect x="8" y="8" width="90" height="26" rx="5" fill="#eab308"/> <text x="53" y="25" text-anchor="middle" fill="#000000" font-family="monospace" font-size="12" font-weight="700">internal</text> <text x="115" y="26" fill="#fef08a" font-size="13" font-weight="600">Faqat joriy Target (Modul) ichida ko'rinadi (Swift sukut bo'yicha darajasi)</text> </g> <g transform="translate(30, 252)"> <rect width="760" height="42" rx="7" fill="url(#gFileprivate)" stroke="#f97316" stroke-width="1.5"/> <rect x="8" y="8" width="95" height="26" rx="5" fill="#f97316"/> <text x="55" y="25" text-anchor="middle" fill="#ffffff" font-family="monospace" font-size="12" font-weight="700">fileprivate</text> <text x="120" y="26" fill="#fed7aa" font-size="13" font-weight="600">Faqat joriy .swift manba fayli ichida ko'rinadi</text> </g> <g transform="translate(30, 302)"> <rect width="760" height="42" rx="7" fill="url(#gPrivate)" stroke="#ef4444" stroke-width="1.5"/> <rect x="8" y="8" width="80" height="26" rx="5" fill="#ef4444"/> <text x="48" y="25" text-anchor="middle" fill="#ffffff" font-family="monospace" font-size="12" font-weight="700">private</text> <text x="105" y="26" fill="#fecaca" font-size="13" font-weight="600">Eng tor doira: Faqat o'rab turgan deklaratsiya (blok/scope) ichida ko'rinadi</text> </g> </svg> </div>
Amaliy Kod Namunasi: package Ishlatilishi
Tasavvur qiling, bizda bitta CoreNetwork paketi ichida NetworkClient va AuthInterceptor targetlari bor. AuthInterceptor faqat tarmoq so'rovlariga yashirin token qo'shishi kerak, lekin ilova UI qatlami bu interceptorni qo'lda chaqirmasligi shart:
// Sources/AuthInterceptor/InternalTokenManager.swift
package struct InternalTokenManager: Sendable {
package let secureToken: String
// "package init" bo'lgani sababli faqat shu paket targetlari chaqira oladi
package init(secureToken: String) {
self.secureToken = secureToken
}
package func generateHeader() -> [String: String] {
return ["Authorization": "Bearer \(secureToken)"]
}
}
// Sources/NetworkClient/APIClient.swift
import AuthInterceptor
public final class APIClient: Sendable {
public init() {}
public func sendRequest(endpoint: String) async throws {
// Shu paket ichidagi boshqa targetdan 'package' structini bemalol chaqiramiz:
let tokenManager = InternalTokenManager(secureToken: "xyz_secret")
let headers = tokenManager.generateHeader()
// So'rov yuborish logikasi...
}
}
Eslatma: Agar asosiy ilovada
import AuthInterceptorqilibInternalTokenManager()chaqirishga urinsangiz, Swift kompilyatori xatolik beradi:'InternalTokenManager' is inaccessible due to 'package' protection level.Bu paket ichki tafsilotlarini 100% germetik himoya qiladi!
4. Katta Loyihalarni Modullashtirish: Interface vs Implementation Pattern
Katta iOS ilovalarida (Uber, Airbnb, Spotify) modullashtirishning eng katta xatosi — bu gorizontal chuqur qaramliklar zanjirini hosil qilishdir (FeatureA -> FeatureB -> FeatureC -> Core). Bunday holatda eng quyi qatlamdagi 1 ta o'zgarish barcha yuqori qatlamlarni qayta kompilyatsiya qilishga majbur qiladi (Cascading Invalidation).
Buning oldini olish uchun Interface / Implementation Separation (Interfeys va Amalga Oshirishni Ajratish) patterni qo'llaniladi.
Arxitektura Sxemasi
<div style="margin: 1.5rem 0; overflow-x: auto;"> <svg viewBox="0 0 840 540" width="100%" style="background: rgba(10, 14, 23, 0.8); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 14px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"> <defs> <linearGradient id="hostGrad" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#4f46e5" stop-opacity="0.3"/> <stop offset="100%" stop-color="#312e81" stop-opacity="0.6"/> </linearGradient> <linearGradient id="implGrad" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#0284c7" stop-opacity="0.25"/> <stop offset="100%" stop-color="#0369a1" stop-opacity="0.45"/> </linearGradient> <linearGradient id="interfaceGrad" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#059669" stop-opacity="0.25"/> <stop offset="100%" stop-color="#047857" stop-opacity="0.5"/> </linearGradient> <linearGradient id="coreGrad" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#d97706" stop-opacity="0.25"/> <stop offset="100%" stop-color="#b45309" stop-opacity="0.45"/> </linearGradient> <marker id="arrowSolid" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"> <path d="M 0 1 L 8 5 L 0 9 z" fill="#38bdf8"/> </marker> <marker id="arrowGreen" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"> <path d="M 0 1 L 8 5 L 0 9 z" fill="#34d399"/> </marker> <marker id="arrowPurple" viewBox="0 0 10 10" refX="6" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"> <path d="M 0 1 L 8 5 L 0 9 z" fill="#818cf8"/> </marker> </defs> <g transform="translate(260, 20)"> <rect width="320" height="70" rx="10" fill="url(#hostGrad)" stroke="#6366f1" stroke-width="2"/> <rect x="14" y="10" width="85" height="18" rx="4" fill="#4f46e5"/> <text x="56" y="23" text-anchor="middle" fill="#ffffff" font-size="9" font-weight="700" letter-spacing="0.5">HOST TARGET</text> <text x="14" y="46" fill="#ffffff" font-size="16" font-weight="700">EcommerceApp</text> <text x="14" y="62" fill="#c7d2fe" font-size="11">Mach-O Executable • DI Container</text> </g> <path d="M 420 90 L 420 115 L 220 115 L 220 140" stroke="#818cf8" stroke-width="1.8" fill="none" marker-end="url(#arrowPurple)"/> <path d="M 420 90 L 420 115 L 620 115 L 620 140" stroke="#818cf8" stroke-width="1.8" fill="none" marker-end="url(#arrowPurple)"/> <rect x="360" y="105" width="120" height="20" rx="4" fill="#1e1b4b" stroke="#6366f1" stroke-width="1"/> <text x="420" y="119" text-anchor="middle" fill="#c7d2fe" font-size="10" font-family="monospace">DI Wiring</text> <g transform="translate(70, 145)"> <rect width="300" height="85" rx="10" fill="url(#implGrad)" stroke="#0284c7" stroke-width="1.8"/> <rect x="14" y="10" width="130" height="18" rx="4" fill="rgba(2, 132, 199, 0.4)"/> <text x="79" y="23" text-anchor="middle" fill="#38bdf8" font-size="9" font-weight="700" letter-spacing="0.5">IMPLEMENTATION</text> <text x="14" y="48" fill="#ffffff" font-size="15" font-weight="700">OrderFeature</text> <text x="14" y="65" fill="#93c5fd" font-size="11">• SwiftUI View & ViewModels</text> <text x="14" y="79" fill="#93c5fd" font-size="11">• Buyurtma berish logikasi</text> </g> <g transform="translate(470, 145)"> <rect width="300" height="85" rx="10" fill="url(#implGrad)" stroke="#0284c7" stroke-width="1.8"/> <rect x="14" y="10" width="130" height="18" rx="4" fill="rgba(2, 132, 199, 0.4)"/> <text x="79" y="23" text-anchor="middle" fill="#38bdf8" font-size="9" font-weight="700" letter-spacing="0.5">IMPLEMENTATION</text> <text x="14" y="48" fill="#ffffff" font-size="15" font-weight="700">CartFeature</text> <text x="14" y="65" fill="#93c5fd" font-size="11">• Savat ekrani & Storage</text> <text x="14" y="79" fill="#93c5fd" font-size="11">• Narxlarni hisoblash</text> </g> <path d="M 370 185 L 470 185" stroke="#ef4444" stroke-width="2" stroke-dasharray="4,4" fill="none"/> <rect x="385" y="173" width="70" height="22" rx="4" fill="#450a0a" stroke="#ef4444" stroke-width="1"/> <text x="420" y="188" text-anchor="middle" fill="#fca5a5" font-size="9" font-weight="700">❌ NO COUPLING</text> <path d="M 220 230 L 220 295" stroke="#34d399" stroke-width="2.2" fill="none" marker-end="url(#arrowGreen)"/> <path d="M 620 230 L 620 295" stroke="#34d399" stroke-width="2.2" fill="none" marker-end="url(#arrowGreen)"/> <g transform="translate(190, 245)"> <rect width="460" height="28" rx="6" fill="#064e3b" stroke="#34d399" stroke-width="1.2"/> <text x="230" y="18" text-anchor="middle" fill="#6ee7b7" font-size="11" font-weight="600">✅ Order faqat yengil CartInterface'ga bog'langan (CartImpl o'zgarsa qayta yig'ilmaydi!)</text> </g> <g transform="translate(70, 305)"> <rect width="300" height="85" rx="10" fill="url(#interfaceGrad)" stroke="#10b981" stroke-width="1.8"/> <rect x="14" y="10" width="130" height="18" rx="4" fill="rgba(16, 185, 129, 0.4)"/> <text x="79" y="23" text-anchor="middle" fill="#a7f3d0" font-size="9" font-weight="700" letter-spacing="0.5">INTERFACE CONTRACT</text> <text x="14" y="48" fill="#ffffff" font-size="15" font-weight="700">CartFeatureInterface</text> <text x="14" y="65" fill="#d1fae5" font-size="11">• CartItemDTO, CartRepositoryProtocol</text> <text x="14" y="79" fill="#a7f3d0" font-size="11" font-weight="600">⚡️ Zero Dependencies • 100% Barqaror</text> </g> <g transform="translate(470, 305)"> <rect width="300" height="85" rx="10" fill="url(#interfaceGrad)" stroke="#10b981" stroke-width="1.8"/> <rect x="14" y="10" width="130" height="18" rx="4" fill="rgba(16, 185, 129, 0.4)"/> <text x="79" y="23" text-anchor="middle" fill="#a7f3d0" font-size="9" font-weight="700" letter-spacing="0.5">INTERFACE CONTRACT</text> <text x="14" y="48" fill="#ffffff" font-size="15" font-weight="700">OrderFeatureInterface</text> <text x="14" y="65" fill="#d1fae5" font-size="11">• OrderDTO, OrderServiceProtocol</text> <text x="14" y="79" fill="#a7f3d0" font-size="11" font-weight="600">⚡️ Zero Dependencies • 100% Barqaror</text> </g> <path d="M 220 390 L 220 425 L 370 425 L 370 445" stroke="#f59e0b" stroke-width="1.8" fill="none" marker-end="url(#arrowSolid)"/> <path d="M 620 390 L 620 425 L 470 425 L 470 445" stroke="#f59e0b" stroke-width="1.8" fill="none" marker-end="url(#arrowSolid)"/> <g transform="translate(260, 445)"> <rect width="320" height="75" rx="10" fill="url(#coreGrad)" stroke="#f59e0b" stroke-width="1.8"/> <rect x="14" y="10" width="140" height="18" rx="4" fill="rgba(245, 158, 11, 0.4)"/> <text x="84" y="23" text-anchor="middle" fill="#fde68a" font-size="9" font-weight="700" letter-spacing="0.5">CORE INFRASTRUCTURE</text> <text x="14" y="48" fill="#ffffff" font-size="15" font-weight="700">DesignSystem & CoreFoundation</text> <text x="14" y="66" fill="#fef3c7" font-size="11">Bundle.module Resurslari • Ranglar • Typography • Umumiy UI</text> </g> </svg> </div>
Nega bu pattern kompilyatsiyani 10x-30x tezlashtiradi?
OrderFeaturesavatdagi mahsulotlarni bilishi kerak. Agar u to'g'ridan-to'g'riCartFeaturega bog'lansa, har safar savat ekranining UI'si, ranglari yoki ichki funksiyasi o'zgargandaOrderFeatureham noldan qayta kompilyatsiya bo'ladi.OrderFeaturefaqatCartFeatureInterface(sof protocol va oddiy structlar) ga bog'lansa, savat ekranining ichki kodlari kuniga 100 marta o'zgarsa ham, OrderFeature keshdan olinadi va bir millisekund ham recompile bo'lmaydi!
Kod Namunasi: Interface va Implementation
// 1. MODUL: CartFeatureInterface (Target: CartFeatureInterface)
import Foundation
public struct CartItemDTO: Identifiable, Sendable, Codable {
public let id: UUID
public let title: String
public let price: Decimal
public init(id: UUID, title: String, price: Decimal) {
self.id = id
self.title = title
self.price = price
}
}
public protocol CartRepositoryProtocol: Sendable {
func getCurrentItems() async throws -> [CartItemDTO]
func clearCart() async throws
}
// 2. MODUL: OrderFeature (Target: OrderFeature)
import SwiftUI
import CartFeatureInterface // Faqat interfeys import qilinadi!
public struct OrderCheckoutView: View {
// Interfeys orqali qabul qilinadi (DIP - Dependency Inversion)
private let cartRepository: any CartRepositoryProtocol
@State private var items: [CartItemDTO] = []
public init(cartRepository: any CartRepositoryProtocol) {
self.cartRepository = cartRepository
}
public var body: some View {
List(items) { item in
HStack {
Text(item.title)
Spacer()
Text("\(item.price) so'm")
}
}
.task {
items = (try? await cartRepository.getCurrentItems()) ?? []
}
}
}
// 3. MODUL: CartFeature (Target: CartFeature - Implementation)
import Foundation
import CartFeatureInterface
public final class RealCartRepository: CartRepositoryProtocol {
public init() {}
public func getCurrentItems() async throws -> [CartItemDTO] {
// Murakkab CoreData, SQLite yoki Network so'rovlari
return [
CartItemDTO(id: UUID(), title: "iPhone 16 Pro", price: 14_500_000)
]
}
public func clearCart() async throws {
// Tozalash logikasi
}
}
Asosiy ilova (EcommerceApp) esa AppContainer ichida ularni bog'lab qo'yadi:
let cartRepo = RealCartRepository()
let orderView = OrderCheckoutView(cartRepository: cartRepo)
5. Build Performance Optimizatsiyasi: Static vs Dynamic Linking va Dyld Launch Time
Ko'pchilik dasturchilar "loyihamni 40 ta modulga bo'ldim, nega ilova sekin ishga tushyapti va build vaqti kamaymadi?" degan savolga duch kelishadi. Sababi — noto'g'ri linking va kompilyator mexanikasini tushunmaslikdir.
5.1. Static Library vs Dynamic Framework
<div style="margin: 1.5rem 0; overflow-x: auto;"> <svg viewBox="0 0 820 220" width="100%" style="background: rgba(15, 23, 42, 0.7); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 12px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"> <defs> <linearGradient id="gStatic" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#22c55e" stop-opacity="0.25"/> <stop offset="100%" stop-color="#15803d" stop-opacity="0.1"/> </linearGradient> <linearGradient id="gDynamic" x1="0" y1="0" x2="1" y2="1"> <stop offset="0%" stop-color="#ef4444" stop-opacity="0.25"/> <stop offset="100%" stop-color="#991b1b" stop-opacity="0.1"/> </linearGradient> </defs> <g transform="translate(25, 20)"> <rect width="370" height="180" rx="10" fill="url(#gStatic)" stroke="#22c55e" stroke-width="2"/> <rect x="16" y="14" width="220" height="22" rx="4" fill="#166534"/> <text x="126" y="29" text-anchor="middle" fill="#86efac" font-size="10" font-weight="700">STATIC LIBRARY (.a / SPM DEFAULT)</text> <text x="16" y="62" fill="#ffffff" font-size="16" font-weight="700">⚡️ App Launch: ~0 ms dyld yuki</text> <text x="16" y="90" fill="#cbd5e1" font-size="12">• Barcha kod bitta executable ichiga pre-link qilinadi</text> <text x="16" y="112" fill="#cbd5e1" font-size="12">• <tspan fill="#86efac" font-weight="700">Dead Code Stripping:</tspan> Ishlatilmagan kod o'chiriladi</text> <text x="16" y="134" fill="#cbd5e1" font-size="12">• Binary hajmi: Kichik va optimallashgan</text> <text x="16" y="162" fill="#4ade80" font-size="12" font-weight="700">✅ Apple tavsiyasi: 95% ilovalar uchun yagona to'g'ri tanlov</text> </g> <g transform="translate(425, 20)"> <rect width="370" height="180" rx="10" fill="url(#gDynamic)" stroke="#ef4444" stroke-width="2"/> <rect x="16" y="14" width="200" height="22" rx="4" fill="#7f1d1d"/> <text x="116" y="29" text-anchor="middle" fill="#fca5a5" font-size="10" font-weight="700">DYNAMIC FRAMEWORK (.dylib)</text> <text x="16" y="62" fill="#ffffff" font-size="16" font-weight="700">🔴 App Launch: +15ms–25ms har biriga</text> <text x="16" y="90" fill="#cbd5e1" font-size="12">• dyld4 yuklash vaqtida page fault, rebase va bind qiladi</text> <text x="16" y="112" fill="#cbd5e1" font-size="12">• Dead code stripping to'liq ishlamaydi (metadata ko'p)</text> <text x="16" y="134" fill="#cbd5e1" font-size="12">• 30 ta dylib = <tspan fill="#f87171" font-weight="700">~600 ms qora ekran kutish lag</tspan></text> <text x="16" y="162" fill="#f87171" font-size="12" font-weight="700">⚠️ Faqat App Extension (Widget) ulashgandagina kerak</text> </g> </svg> </div>
| Ko'rsatkich | 🟢 Static Library (.a) | 🔴 Dynamic Framework (.dylib) |
|---|---|---|
| Kompilyatsiya vaqti | Juda tez (Module kesh) | Har biriga alohida dylib yig'iladi |
| Final Link vaqti | Biroz ko'proq linker yuki | Tezroq link bo'ladi |
| App Cold Start (dyld) | ⚡️ Deyarli 0 ms qo'shimcha yuk | 🔴 Har bir dylib +15–20ms kechikish |
| Dead Code Stripping | ✅ To'liq ishlaydi (Hajm kichrayadi) | ❌ Ishlatilmagan kod ham binar ichida qoladi |
| Binary Size (IPA) | Odatda ancha kichik | Katta (Dylib metadata va headerlar ko'p) |
| Duplicate Symbols xavfi | Ehtiyotkorlik talab etadi (Extensionlarda) | Izolyatsiya qilingan |
Apple Rasmiy Dyld Tavsiyasi
Apple muhandislari WWDC sessiyalarida aniq ko'rsatgan:
- Ilovada 6 tadan ortiq Dynamic Framework bo'lmasligi kerak.
- Har bir Dynamic Framework iOS tizimidagi dinamik linker (
dyld4) tomonidan ilova ochilishida (pre-main vaqtida) xotiraga yuklanishi, sahifalarga xaritalanishi (mmap), rebase va bind qilinishi kerak. - Agar sizda 30 ta dynamic framework bo'lsa, ilovangiz ochilishi 450–600 ms kechikadi! Foydalanuvchi qora ekranni ko'rib turadi.
- Xulosa: Swift Package Manager'da
.library(name: "...", targets: ["..."])deklaratsiyasidatype:parametrini ko'rsatmang yoki uni.staticqiling. SPM o'zi sukut bo'yicha uni eng maqbul static kutubxona sifatida link qiladi.
5.2. Diamond Dependency (Duplicate Symbols) Muammosi
Agar ilovangizda ikkita alohida Dynamic Framework (masalan, WidgetExtension.framework va MainApp.framework) bir xil static SPM kutubxonasiga bog'lansa:
- Static kutubxona kodi ikkala framework ichiga ham nusxalanadi (duplicate).
- Bu xotirani 2 marta band qiladi va runtime vaqtida
Class ... is implemented in both ... One of the two will be used.ogohlantirishini chiqaradi.
Yechim: Agar biror modul ham App, ham Widget, ham boshqa App Extension tomonidan bir vaqtda ishlatilishi shart bo'lsa, faqatgina o'sha umumiy modulni .library(name: "SharedKit", type: .dynamic, targets: ["SharedKit"]) deb dynamic qilib belgilang. Qolgan barcha featurelar static bo'lib qolishi shart!
6. Resurslar, Assetlar va Lokalizatsiya (Bundle.module)
SPM paydo bo'lgunga qadar, alohida kutubxonalarda rasm, rang yoki matn saqlash o'ta mashaqqatli edi. SPM 5.3 dan boshlab Resurslarni avtomatik qo'llab-quvvatlash tizimi kiritildi.
6.1. .process vs .copy
.process("Resources"): Tavsiya etiladi. Xcode resurslarni platformaga moslab optimizatsiya qiladi (PNG rasmlarni siqadi, Asset Cataloglarni.carformatiga kompilyatsiya qiladi,Stringsfayllarni binarizatsiya qiladi)..copy("Data.json"): Faylni hech qanday o'zgarishsiz, asl holicha paket ichiga nusxalaydi.
6.2. Bundle.module Ishlatilishi
SPM biror targetda resources qatorini ko'rsa, avtomatik tarzda resource_bundle_accessor.swift faylini generatsiya qiladi va unga Bundle.module xossasini kiritadi.
// Sources/DesignSystem/UIComponents/AppLogoView.swift
import SwiftUI
public struct AppLogoView: View {
public init() {}
public var body: some View {
// Image("logo") qilsangiz izlash Asosiy ilovadan ketadi va topolmaydi!
// Doimo Bundle.module ko'rsatilishi shart:
Image("CompanyLogo", bundle: .module)
.resizable()
.scaledToFit()
.frame(width: 120, height: 120)
.background(Color("BrandPrimaryColor", bundle: .module))
}
}
Lokalizatsiyalangan matnlar uchun:
Text("welcome_title", bundle: .module)
// Yoki Swift 6 zamonaviy String(localized:):
let message = String(localized: "checkout_success_message", bundle: .module)
7. Yopiq Kodli Kutubxonalar: .binaryTarget & XCFrameworks
Ko'pincha bank, to'lov tizimlari yoki maxfiy C++ algoritmlarining manba kodi ochiq bo'lmaydi. Ular .xcframework binar ko'rinishida beriladi. SPM ularni mukammal integratsiya qiladi.
7.1. Katta Kompaniyalar Nega Binar Tarqatadi?
- Google Maps & Yandex MapKit: 3D shahar xaritasi render dvijogi, Metal/Vulkan sharderlari va xususiy C++ algoritmlarini ochiqlamaslik (IP protection).
- Google Mobile Ads (AdMob) & Meta Audience Network: Reklama sohasida "Ad Fraud" (soxta bosishlar, bot trafik)ning oldini olish uchun yopiq token almashinuvi va telemetriya.
- Stripe, PayPal & Bank SDK lari: PCI-DSS xavfsizlik talablariga ko'ra karta tokenizatsiyasi va biometrik barmoq izi tekshiruvlarini yashirish.
- B2B Litsenziyalash: O'z intellektual modulingizni boshqa korxonalarga manba kodini bermasdan, shartnoma asosida pullik litsenziyalash.
7.2. XCFramework Yig'ish va Checksum Hisoblash:
Binary framework yaratishda quyidagi ikki flag muhim:
BUILD_LIBRARY_FOR_DISTRIBUTION=YES— Swift Module Stability'ni yoqadi (.swiftinterfacehosil qilib, kelajakdagi Swift versiyalarida ham binar buzilmasligini kafolatlaydi).STRIP_STYLE=all&SEPARATE_STRIP=YES— Barcha private va debug belgilarni qirqib tashlaydi.
Terminalda zip arxivining xavfsiz SHA256 nazorat summasi olinadi:
swift package compute-checksum PaymentCore-v3.4.0.xcframework.zip
# Natija: a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890
7.3. Manifestda Belgilash:
targets: [
// Masofaviy binar target
.binaryTarget(
name: "PaymentCoreBinary",
url: "https://cdn.mybank.uz/sdk/ios/PaymentCore-v3.4.0.xcframework.zip",
checksum: "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890"
),
// Swift Wrapper (Qulay SwiftUI interfeysi beruvchi yengil modul)
.target(
name: "PaymentSDK",
dependencies: ["PaymentCoreBinary"]
)
]
💡 Batafsil amaliyot: Anti-debugging (
ptrace), compile-time XOR string maskalash va Public Key SSL Pinning bo'yicha to'liq 4 qatlamli xavfsizlik arxitekturasi [15. [Demo Hard] FinTech SuperApp](/ios-lessons/spm-fintech-superapp) darsida to'liq amaliy kod bilan yoritilgan.
8. Swift Package Plugins & Swift Macros
SPM shunchaki qaramliklarni yuklovchi vosita emas, u to'liq kengaytiriladigan Metaprogramming va Tooling platformasidir.
8.1. Build Tool Plugins (Kompilyatsiya vaqtida kod generatsiyasi)
Loyiha har safar yig'ilganda (Build), SwiftLint xatolarini tekshirishi yoki OpenAPI / Protobuf fayllaridan avtomat Swift kodini chiqarishi mumkin.
// Plugins/GenerateAPIPlugin/Plugin.swift
import PackagePlugin
@main
struct GenerateAPIPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
let generatorTool = try context.tool(named: "api-codegen")
let specFile = target.directory.appending(subpath: "openapi.yaml")
let outputFile = context.pluginWorkDirectory.appending(subpath: "GeneratedAPI.swift")
return [
.buildCommand(
displayName: "Generating Swift API from OpenAPI spec",
executable: generatorTool.path,
arguments: ["--input", specFile.string, "--output", outputFile.string],
inputFiles: [specFile],
outputFiles: [outputFile]
)
]
}
}
8.2. Swift 6 Macros Paketlari
Swift 5.9/6.0 dagi mashhur @Observable, @FreelySendable makrolari ham to'liq SPM package sifatida yaratiladi. Ular CompilerPluginSupport moduli va swift-syntax kutubxonasiga tayanadi.
9. Kompilyatsiya Diagnostikasi va Compiler Flaglar
Loyihangizdagi eng sekin kompilyatsiya bo'layotgan joylarni topish uchun quyidagi diagnostika bayroqlarini (flags) ishlating:
9.1. Sekin funksiyalar va ifodalarni topish
Package.swift ichida swiftSettings ga qo'shing yoki Xcode Build Settings da kiriting:
swiftSettings: [
// 100 millisekunddan ko'proq vaqt olgan ifodalar haqida ogohlantirish
.unsafeFlags([
"-Xfrontend", "-warn-long-expression-type-checking=100",
"-Xfrontend", "-warn-long-function-bodies=100"
], .when(configuration: .debug))
]
Muhim tajriba: SwiftUI kodlarida
+yoki murakkab ternar operatorlar bilan yozilgan ViewBuilder ifodalarida tipni aniqlash (Type Inference) kompilyatorni 5-10 sekundgacha qotirib qo'yishi mumkin! Aniq tiplarni ko'rsatish (let title: String = ...) bu vaqtni 100 barobarga qisqartiradi.
9.2. Build Timing Summary
Terminal orqali butun yig'ilish jarayonini sekundma-sekund tahlil qilish:
xcodebuild -scheme EcommerceApp -destination 'generic/platform=iOS' \
-showBuildTimingSummary build
Natija quyidagicha jadval ko'rinishida chiqadi:
CompileSwiftSources (OrderFeature) : 4.2s
CompileSwiftSources (CartFeature) : 3.1s
CompileSwiftSources (DesignSystem) : 1.8s
Ld (EcommerceApp) : 0.9s
Total Build Time : 10.0s (Avvalgi monolitda: 3m 42s)
10. Dunyo Tajribasi: Apple, Airbnb, Uber va Point-Free
10.1. Apple (Swift Open Source)
Apple kompaniyasi swift-syntax, swift-testing va swift-format kabi rasmiy vositalarini bitta monorepo ichida o'nlab alohida mikromodullarga ajratgan. Har bir sintaksis daraxti alohida target bo'lib, Swift 6 Concurrency tekshiruvidan to'liq o'tgan. Bu har bir parallel CPU yadrosiga alohida modulni berib, kompilyatsiyani parallelizatsiya qilish imkonini beradi.
10.2. Airbnb (40+ Feature Modules & Vertical Slicing)
Airbnb muhandislari monolitdan SPM ga o'tishda avvaliga hamma narsani gorizontal bo'lishgan (Core, Common, Utils). Natijada biror util o'zgarsa, hamma narsa qayta kompilyatsiya bo'lgan. Keyinchalik ular Vertical Slicing va Interface/Impl yondashuviga o'tib, har bir funksional imkoniyatni (Search, Booking, Messages) vertikal mustaqil paketga aylantirishgan. Natijada CI build vaqti 70% ga qisqargan.
10.3. Uber (RIBs va Dyld Halokati)
Uber ilovasi yuzlab Dynamic Frameworklarga bo'linganda, ilovaning ochilishi (Cold Launch) 5-6 sekundga cho'zilib ketgan. Ular butun infratuzilmani static kutubxonalar va SPM ga o'tkazib, ilova ishga tushish vaqtini 0.35 sekundga (sub-second) tushirishga muvaffaq bo'lishgan.
10.4. Point-Free (TCA Micro-Packages)
Stephen Celis va Brandon Williams swift-composable-architecture kutubxonasini o'nlab mayda paketlarga ajratgan: CasePaths, CustomDump, Dependencies, Clocks, IdentifiedCollections. Buning sharofati bilan testlarni ishga tushirishda og'ir UI kutubxonalari kompilyatsiya qilinmaydi va unit testlar 50-100 millisekundda yakunlanadi.
11. CI/CD va SPM Build Cache Strategiyasi
GitHub Actions, GitLab CI yoki Xcode Cloud'da tezkor build olish uchun SPM keshini to'g'ri boshqarish zarur:
# GitHub Actions misoli
- name: Cache Swift PM Packages
uses: actions/cache@v4
with:
path: |
.build
~/Library/Caches/org.swift.swiftpm
~/Library/Developer/Xcode/DerivedData/**/SourcePackages
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
${{ runner.os }}-spm-
Package.resolved faylining hashiga qarab kesh saqlansa, har bir commitda tashqi kutubxonalarni qayta internetdan yuklab olishga vaqt sarflanmaydi (har bir CI run'da 3–5 daqiqa tejaladi).
12. Xulosa: Senior Muhandisning 10 Oltin Qoidasi
- Monorepo afzal: 50 ta alohida git repozitoriy emas, bitta repozitoriy ichida ko'p targetli SPM paketlari yarating.
- Interface va Implementation ajratilsin: Har bir yirik feature uchun alohida
Interfacetargeti oching va faqat protocollarga bog'laning. - Static by default: Ilova modullarini doimo static qilib qoldiring. Dyld launch vaqtini o'g'irlamang.
- **
packageaccess levelidan foydalaning (SE-0386):** Hammasinipublicqilib yubormang. - Swift 6 Strict Concurrency yoqing: Barcha modullarda
.enableUpcomingFeature("StrictConcurrency")orqali data racelardan xoli bo'ling. - **Resurslarni
Bundle.moduleorqali chaqiring:** Hech qachonBundle.mainga tayanmang. - Explicit Type Annotation: Murakkab ifodalarda doimo tiplarni aniq ko'rsating, kompilyator inference jarayonini sekinlashtirmang.
- Izolyatsiyalangan Micro-Applar: Har bir feature uchun kichik executable target yoki preview ilova yarating, butun ilovani build qilmasdan tezkor ishlab chiqing.
- Keshdan unumli foydalaning: CI runnerlarda
SourcePackagespapkasini to'liq keshlang. - Doimiy profayling: Har haftada
showBuildTimingSummaryorqali sekinlashgan modullarni aniqlab, qaramliklarni optimallashtiring.