# Tuist 04: Dependency'lar — target'dan Registry'gacha

> **Holat:** 2026-yil oktabr. Bu dars Tuist loyihasida dependency'larning hamma turini — ichki target, boshqa project, binary framework/XCFramework, system SDK, Swift Package va Swift macro — qanday e'lon qilish, ularni `tuist install` bilan yuklash, `tuist graph` bilan ko'rish va `tuist inspect dependencies` bilan nazorat qilishni o'rgatadi. Kod misollari **Tuist 4.211.1**, **Xcode 27.1 (Swift 6.4)** va **Apple M4 Pro (12 core, 24 GB)** da tekshirilgan.

> **Bu darsdan keyin siz:** loyihangizning butun dependency grafigini aniq, statik va tekshiriladigan qilib boshqara olasiz — qaysi modul qaysisiga bog'langanini bir qarashda bilasiz va tasodifiy ("implicit") bog'lanishlarni CI'da avtomatik tuta olasiz.

- `TargetDependency`ning barcha 10 holatini (`.target`, `.project`, `.framework`, `.library`, `.xcframework`, `.sdk`, `.xctest`, `.external`, `.package`, `.macro`) qachon ishlatishni bilasiz.
- Tashqi Swift Package'larni ikki xil yo'l bilan (Xcode native va Tuist XcodeProj-based) ulashni va orasidagi farqni tushunasiz.
- `tuist install` paketlarni aynan qayerga yuklashini (va rasmiy hujjat bu yerda eskirganini) o'z ko'zingiz bilan ko'rasiz.
- `static` va `dynamic` linking farqini, "static side effect" ogohlantirishini va uni qachon jiddiy qabul qilishni bilasiz.
- `tuist inspect dependencies` bilan implicit import'ni atayin yaratib, uni real xato bilan tutasiz.
- Swift Package Registry resolve'ni nega tezlashtirishini va u qanday hisob talab qilishini bilasiz.

## Mundarija

1. [Nega dependency grafigi muhim — Tuist nimani boshqacha qiladi](#nega-graf)
2. [Local dependency'lar — `.target` va `.project`](#local)
3. [Binary va system dependency'lar — framework, library, XCFramework, SDK](#binary)
4. [Tashqi paketlar — Swift Package'lar va `tuist install`](#external)
5. [`PackageSettings` — paketlarni sozlash](#package-settings)
6. [`static` yoki `dynamic` — va "static side effect"](#static-dynamic)
7. [Swift macro'lar — tashqi va o'zingniki](#macros)
8. [`tuist graph` — grafikni ko'rish](#graph)
9. [`tuist inspect dependencies` — implicit import'ni tutish](#inspect)
10. [Swift Package Registry — resolve'ni tezlashtirish](#registry)
11. [Troubleshooting — `-ObjC`, Firebase, TCA, CocoaPods](#troubleshooting)
12. [Amaliy mashqlar](#mashqlar)
13. [O'zingizni tekshiring](#checklist)
14. [Xulosa](#xulosa)
15. [Manbalar](#manbalar)

---

## 1. Nega dependency grafigi muhim — Tuist nimani boshqacha qiladi {#nega-graf}

Loyiha o'sgani sari uni bir nechta target'ga bo'lamiz: kodni qayta ishlatish, chegaralarni belgilash va build vaqtini qisqartirish uchun. Ko'p target degani — ular orasida **dependency** (bog'lanish) e'lon qilish degani. Shu bog'lanishlar yig'indisi **dependency graph** deyiladi, va u loyihangizning tashqi paketlarni ham o'z ichiga oladi.

Muammo shundaki, Xcode va XcodeProj grafikni qo'lda saqlashni og'ir va xatoga moyil qiladi. Tuist hujjatlari aynan shu uch og'riqni sanaydi (`dependencies.md`):

- Xcode build system barcha target mahsulotlarini bitta `DerivedData` papkasiga chiqaradi. Shuning uchun bir target o'ziga **ruxsat berilmagan** modulni ham `import` qila oladi — mahalliy mashinada build o'tadi, lekin CI'dagi toza build'da yoki boshqa configuration'da kutilmaganda sinadi.
- Dinamik target'ning transitive (bilvosita) bog'lanishlari `LD_RUNPATH_SEARCH_PATHS` ro'yxatidagi papkalardan biriga nusxalanishi kerak, aks holda ular runtime'da topilmaydi. Grafik kichik bo'lsa buni esda tutish oson, kattalashganda esa muammoga aylanadi.
- Target statik XCFramework'ni link qilsa, Xcode uchun paketni qayta ishlab, kerakli binary'ni chiqaruvchi qo'shimcha build phase kerak — u avtomatik qo'shilmaydi va uni esdan chiqarish oson.

Tuist'ning falsafasi bitta jumlada: **dependency graph aniq (explicit) va statik bo'lishi kerak, chunki faqat shundagina u tekshirilishi va optimallashtirilishi mumkin.** Siz "nima nimaga bog'liq"ni tasvirlaysiz, qolganini (RUNPATH, build phase'lar, embed qilish) Tuist generatsiya paytida o'zi hal qiladi.

> **🧠 Esda tuting:** Tuist graph'ni **generatsiya paytida** validatsiya qiladi — cycle (aylanma bog'lanish) bo'lsa yoki bog'lanish noto'g'ri bo'lsa, `tuist generate` to'xtaydi. Bu butun jamoaga grafikni buzmasdan o'zgartirish imkonini beradi.

> **🚀 Motivatsiya:** Toss jamoasi (~1,000 modul) binary cache'dan foyda ko'rishdan **oldin** grafikni "cacheable" qilishga vaqt sarfladi: ishlatilmagan import'larni avtomatik olib tashladi, uchinchi tomon paketlarni alohida repozitoriyga ko'chirdi va har bir PR'da cacheable-modul ulushi tushib ketsa ogohlantiruvchi tekshiruv qo'shdi. Tuist'ning maslahati: implicit import'larni statik tahlil bilan tuting, chunki explicit modules va CAS faqat implicit bog'lanishlar yo'q bo'lgandagina ishlaydi (manba: Toss case study, 2026-10; Tuist "Scaling" blogi, 2025-09-22). Bu darsning 9-bo'limida siz aynan shu tekshiruvni ishga tushirasiz.

Grafikdagi bog'lanishlarning hammasi `Target`ning `dependencies:` parametrida e'lon qilinadi. Keling, uning har bir turini ko'rib chiqaylik.

![Tuist TargetDependency turlarining xaritasi: local (target, project), binary (framework, library, xcframework), system (sdk, xctest) va external (external, package, macro) guruhlarga bo'lingan](/lessons/images/tuist/tuist_04_dependency_kinds.svg)

Yuqoridagi xarita `TargetDependency`ning barcha holatlarini to'rt guruhga ajratadi: loyiha ichidagi **local** bog'lanishlar, oldindan kompilyatsiya qilingan **binary**'lar, Apple'ning **system** framework'lari va tashqi **external** paketlar. Keyingi bo'limlarda har birini real kod bilan ko'ramiz.

---

## 2. Local dependency'lar — `.target` va `.project` {#local}

Local dependency — loyihangiz **ichidagi** boshqa target yoki boshqa project'dagi target'ga bog'lanish. Bu eng ko'p ishlatiladigan tur: modulyar loyihaning asosi.

### 2.1 `.target(name:)` — bir project ichida

Eng oddiy holat: bitta `Project.swift` ichidagi ikki target. Quyida biz haqiqatan build qilgan loyiha — valyuta formatlovchi `Core` moduli va uni ishlatuvchi ilova:

```swift
// Project.swift
import ProjectDescription

let project = Project(
    name: "DepsDemo",
    targets: [
        // Core — hech kimga bog'liq emas, sof Swift modul
        .target(
            name: "Core",
            destinations: .iOS,
            product: .staticFramework,
            bundleId: "uz.azimjondev.depsdemo.core",
            deploymentTargets: .iOS("17.0"),
            sources: ["Core/Sources/**"]
        ),
        // RatesUI — Core ga bog'liq
        .target(
            name: "RatesUI",
            destinations: .iOS,
            product: .framework,
            bundleId: "uz.azimjondev.depsdemo.ratesui",
            deploymentTargets: .iOS("17.0"),
            sources: ["RatesUI/Sources/**"],
            dependencies: [
                .target(name: "Core")   // ← local dependency
            ]
        ),
    ]
)
```

`Core` — sof Swift modul, hech kimga bog'liq emas. Uning ichida oddiy public API:

```swift
// Core/Sources/MoneyFormatter.swift
import Foundation

public enum MoneyFormatter {
    public static func format(_ amount: Decimal, code: String) -> String {
        let f = NumberFormatter()
        f.numberStyle = .decimal
        f.maximumFractionDigits = 2
        let n = f.string(from: amount as NSDecimalNumber) ?? "0"
        return "\(n) \(code)"
    }
}
```

`RatesUI` endi o'z kodida `import Core` qila oladi va `Core`ning public API'sidan foydalanadi:

```swift
// RatesUI/Sources/RatesView.swift
import UIKit
import Core

public final class RatesView: UIView {
    private let label = UILabel()
    public override init(frame: CGRect) {
        super.init(frame: frame)
        addSubview(label)
        label.text = MoneyFormatter.format(1250, code: "UZS")  // Core'dan
    }
    required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }
}
```

> **💡 Pro tip:** `.target`ning ikkita shakli bor. `.target(name: "Core")` — nom bilan. Agar target obyektini o'zgaruvchiga olgan bo'lsangiz, `.target(_ target:)` sintaktik shakli ham bor: u ichki tomondan `.target(name: target.name)`ga aylanadi (manba: `TargetDependency.swift`). Nom bilan yozish ko'pincha soddaroq.

### 2.2 Transitive (bilvosita) bog'lanish

Agar `App` → `RatesUI` → `Core` bo'lsa, `App` faqat `RatesUI`ga bog'lansa yetarli. `Core` **transitive** (bilvosita) tarzda keladi — uni link qilish, embed qilish va RUNPATH'ni to'g'rilashni Tuist o'zi bajaradi.

> **⚠️ Diqqat:** Transitive kelishi "import qila olaman" degani **emas**. `App` o'z kodida `import Core` qilmoqchi bo'lsa, `Core`ni `App`ning `dependencies:`iga **ham** aniq qo'shishi kerak. Aks holda bu "implicit dependency" bo'ladi — build tasodifan o'tishi mumkin, lekin 9-bo'limdagi `tuist inspect dependencies` buni xato deb topadi. Shuning uchun bizning `App` target'imiz `Core`ni ham, `RatesUI`ni ham to'g'ridan-to'g'ri e'lon qiladi.

### 2.3 `.project(target:path:)` — boshqa project'dagi target

Katta loyihalar bir nechta `Project.swift`ga bo'linadi (buni [06. Modular arxitektura](/ios-lessons/tuist-06-modular-architecture) darsida chuqur ko'ramiz). Boshqa papkadagi project'ning target'iga shunday bog'lanasiz:

```swift
// Signature (manba: TargetDependency.swift):
// case project(target: String, path: Path, status: LinkingStatus = .required, condition: PlatformCondition? = nil)

dependencies: [
    .project(
        target: "HomeFeature",
        path: "../Features/Home"   // Project.swift joylashgan papkaga nisbiy yo'l
    )
]
```

`path` — bog'langan `Project.swift` turgan **papka** yo'li (faylning emas). Bu bog'lanish ishlashi uchun ikkala project ham bitta `Workspace` da bo'lishi yoki ildiz `Project.swift`dan topilishi kerak.

### 2.4 `status` — majburiy, optional yoki link qilinmaydigan

`.target`, `.project`, `.framework`, `.xcframework` va `.sdk` holatlari `status` parametrini oladi. U `LinkingStatus` enum'i (manba: `TargetDependency.swift`):

```swift
public enum LinkingStatus: String, Codable, Hashable, Sendable {
    case required   // doim link qilinadi (default)
    case optional   // weak link — symbol bo'lmasa crash emas, nil
    case none       // link qilinmaydi, faqat build order uchun
}
```

`.optional` — framework runtime'da bo'lmasligi mumkin bo'lgan holatlar uchun (masalan, yangi OS API'si eski qurilmada yo'q). U `-weak_framework` bilan link qiladi.

### 2.5 `condition` — platforma bo'yicha shartli bog'lanish

Har bir dependency turi `condition` parametrini oladi. Default'da target qo'llab-quvvatlaydigan hamma platformaga link qilinadi. Faqat ma'lum platformada kerak bo'lsa:

```swift
dependencies: [
    .target(name: "iOSOnlyModule", condition: .when([.ios])),
    .sdk(name: "WatchKit", type: .framework, condition: .when([.watchos])),
]
```

Bu multi-platform target'larda (bitta target iOS + watchOS + macOS) juda muhim — keraksiz framework'ni noto'g'ri platformaga link qilmaslik uchun.

---

## 3. Binary va system dependency'lar — framework, library, XCFramework, SDK {#binary}

Hamma dependency ham source kod bo'lavermaydi. Ko'pincha sizga **oldindan kompilyatsiya qilingan binary** (vendor bergan SDK) yoki **Apple'ning system framework'i** kerak bo'ladi. `TargetDependency` buning uchun to'rt holatni beradi.

### 3.1 `.framework(path:)` — tayyor `.framework`

```swift
// case framework(path: Path, status: LinkingStatus = .required, condition: PlatformCondition? = nil)
dependencies: [
    .framework(path: "Frameworks/MySDK.framework")
]
```

Bitta platforma uchun kompilyatsiya qilingan dinamik yoki statik framework. `status: .optional` bilan weak link qilsa bo'ladi.

### 3.2 `.library(path:publicHeaders:swiftModuleMap:)` — tayyor kutubxona

C/Objective-C statik kutubxonasi (`.a`) uchun. Unga header'lar yo'li va (ixtiyoriy) Swift module map kerak:

```swift
// case library(path: Path, publicHeaders: Path, swiftModuleMap: Path?, condition: PlatformCondition? = nil)
dependencies: [
    .library(
        path: "Libraries/libparser.a",
        publicHeaders: "Libraries/include",
        swiftModuleMap: "Libraries/module.modulemap"
    )
]
```

### 3.3 `.xcframework(path:)` — ko'p platformali binary

`.xcframework` — bitta paketda bir nechta platforma/arxitektura slice'ini saqlaydigan Apple formati (iOS qurilma + iOS simulator + macOS...). Zamonaviy vendor SDK'lar shu formatda tarqatiladi:

```swift
// case xcframework(path:, expectedSignature:, status:, condition:)
dependencies: [
    .xcframework(path: "Frameworks/Analytics.xcframework")
]
```

Tuist statik XCFramework uchun kerak bo'lgan maxsus build phase'ni avtomatik qo'shadi — bu Xcode'da qo'lda qilishda eng ko'p esdan chiqadigan narsa.

**Imzo tekshiruvi (`expectedSignature`).** XCFramework imzolangan bo'lsa, Tuist uning haqiqiyligini tekshirishi mumkin. `XCFrameworkSignature` enum'i uch holat beradi (manba: `TargetDependency.swift`):

```swift
dependencies: [
    // Apple sertifikati bilan imzolangan:
    .xcframework(
        path: "Frameworks/Vendor.xcframework",
        expectedSignature: .signedWithAppleCertificate(
            teamIdentifier: "ABCDE12345",
            teamName: "Vendor Inc."
        )
    ),
]
```

Yana ikki holat: `.unsigned` (imzolanmagan) va `.selfSigned(fingerprint:)` (o'z-o'zini imzolagan). Agar hisoblangan imzo kutilganidan farq qilsa, Tuist generatsiyani to'xtatadi — bu supply-chain hujumiga qarshi himoya.

### 3.4 `.sdk(name:type:status:)` — Apple system framework

Apple'ning system framework va kutubxonalari uchun: `AVFoundation`, `StoreKit`, `libz` va h.k. `SDKType` enum'i uch tur beradi: `.framework`, `.library`, `.swiftLibrary`.

Bizning tekshirilgan loyihamizda `App` target'iga `AVFoundation`ni shunday qo'shdik:

```swift
dependencies: [
    .target(name: "Core"),
    .target(name: "RatesUI"),
    .sdk(name: "AVFoundation", type: .framework),   // ← system framework
]
```

`.sdk`ning ikki shakli bor (manba: `TargetDependency.swift`):

```swift
// To'liq: status bilan
.sdk(name: "c++", type: .library, status: .required)
// Qisqa: status default .required bo'ladi
.sdk(name: "AVFoundation", type: .framework)
```

> **🧠 Esda tuting:** `UIKit`, `SwiftUI`, `Foundation` kabi eng asosiy framework'lar **avtomatik** keladi — ularni `.sdk` bilan qo'shish shart emas. `.sdk` faqat qo'shimcha system framework'lar (AVFoundation, StoreKit, CoreBluetooth...) uchun kerak.

### 3.5 `.xctest` — test framework'i

Test target'larida XCTest'ni aniq qo'shish uchun (odatda `product: .unitTests` buni o'zi beradi, lekin ba'zi hollarda kerak):

```swift
dependencies: [
    .xctest
]
```

> **📊 Real raqamlar:** Yuqoridagi `DepsDemo` loyihasi — `Core` (staticFramework) + `RatesUI` (framework, SnapKit'ga bog'liq) + `App` (app, AVFoundation'ga bog'liq) — Apple M4 Pro'da, Xcode 27.1, Tuist 4.211.1 bilan `xcodebuild ... build` orqali `** BUILD SUCCEEDED **` natijasini berdi. Build log'da `Core`, `RatesUI`, `App` va tashqi `SnapKit` target'lari (arm64 + x86_64) kompilyatsiya qilinib, `DepsDemo.app` va `RatesUI.framework` chiqdi. Ya'ni yuqoridagi barcha dependency turlari (local `.target`, external, `.sdk`) bir loyihada birga ishlaydi.

---

## 4. Tashqi paketlar — Swift Package'lar va `tuist install` {#external}

Endi eng ko'p ishlatiladigan tashqi dependency turiga — **Swift Package**'larga o'tamiz. Tuist buni ikki xil mexanizm bilan ulaydi, va orasidagi farqni tushunish katta loyihada juda muhim.

### 4.1 Ikki integratsiya: Xcode native vs Tuist XcodeProj-based

|                             | Xcode native integratsiya                        | Tuist XcodeProj-based integratsiya                       |
| --------------------------- | ------------------------------------------------ | -------------------------------------------------------- |
| **Qanday e'lon qilinadi**   | `Project(packages:)` + `.package(product:type:)` | `Tuist/Package.swift` + `.external(name:)`               |
| **Paket qanday ulanadi**    | Xcode'ning yopiq SPM mexanizmi                   | Har bir paket XcodeProj target'iga aylanadi (source'dan) |
| **Binary cache bilan**      | ❌ ishlamaydi                                    | ✅ ishlaydi                                              |
| **Selective testing bilan** | ❌                                               | ✅                                                       |
| **Yangi SPM feature'lari**  | Darhol (Apple qiladi)                            | Ba'zan kechikadi (Tuist mapping kerak)                   |
| **Kim saqlaydi**            | Apple (yopiq manba)                              | Tuist (ochiq manba, hamjamiyat hissa qo'shadi)           |

Tuist hujjati buni to'g'ridan-to'g'ri aytadi: Xcode native integratsiya **eng qulay**, lekin o'rta va katta loyihalar uchun kerakli **moslik va nazoratdan mahrum**. XcodeProj-based integratsiya paketni XcodeProj target'lariga aylantirgani uchun uni cache va selective testing bilan birga ishlatish mumkin bo'ladi.

Shuning uchun **tavsiya etiladigan yo'l — `.external(name:)`** (XcodeProj-based). Keyingi bo'limlarda shuni ishlatamiz; Xcode native yo'lni esa macro va build-tool plugin'lar uchun saqlab qo'yamiz (7-bo'lim).

### 4.2 `Tuist/Package.swift` — tashqi paketlar interfeysi

Tashqi paketlar `Tuist/Package.swift` faylida e'lon qilinadi (ildizda ham bo'lishi mumkin). Diqqat qiling: bu fayl faqat **dependency ro'yxati** — unda hech qanday `targets` yoki `products` yozilmaydi.

Quyida biz haqiqatan ishlatgan fayl — SnapKit paketini qo'shadi:

```swift
// Tuist/Package.swift
// swift-tools-version: 6.0
import PackageDescription

#if TUIST
import ProjectDescription

let packageSettings = PackageSettings(
    productTypes: [
        "SnapKit": .staticFramework,   // default ham .staticFramework
    ]
)
#endif

let package = Package(
    name: "DepsDemo",
    dependencies: [
        .package(url: "https://github.com/SnapKit/SnapKit", from: "5.7.1"),
    ]
)
```

> **🧠 Esda tuting:** `#if TUIST` bloki — Tuist uchun maxsus. Oddiy `swift build` bu blokni ko'rmaydi (chunki `TUIST` compilation flag'i faqat Tuist manifestni kompilyatsiya qilganda yoqiladi). Shu blok ichida `PackageSettings` orqali har bir paketning product type'ini va boshqa sozlamalarni belgilaysiz. Default'da bu blok kerak emas — faqat sozlash kerak bo'lganda yozasiz.

### 4.3 `tuist install` — va paketlar aynan qayerga tushadi

Paketlarni yuklash alohida buyruq — xuddi CocoaPods'dagi `pod install` kabi. Bu sizga paketlar qachon resolve/update bo'lishini nazorat qilish imkonini beradi:

```bash
tuist install
```

Bizning mashinamizda real natija:

```text
Resolving and fetching plugins.
Resolving and fetching dependencies.
Fetching https://github.com/SnapKit/SnapKit
[1/4295] Fetching snapkit
Fetched https://github.com/SnapKit/SnapKit (2.04s)
Computing version for https://github.com/SnapKit/SnapKit
Computed https://github.com/SnapKit/SnapKit at 5.7.1 (2.77s)
Creating working copy for https://github.com/SnapKit/SnapKit
Working copy of https://github.com/SnapKit/SnapKit resolved at 5.7.1
✔ Success
  Plugins resolved and fetched successfully.
```

> **⚠️ Diqqat — rasmiy hujjat bu yerda eskirgan:** Tuist'ning rasmiy hujjati hali ham "paketlar `Tuist/Dependencies` papkasiga tushadi" deydi. Bu **Tuist 3 davridan qolgan eski ma'lumot.** Biz 4.211.1 da o'z ko'zimiz bilan tekshirdik — paketlar aslida SwiftPM'ning `.build` papkasiga, ya'ni **`Tuist/.build`** ga tushadi:

```text
$ ls -a Tuist/
.build/              ← bu yerga tushadi (checkouts, repositories, tuist-derived)
Package.resolved     ← lock fayl shu yerda yaratiladi
Package.swift

$ find Tuist/.build -maxdepth 2 -iname "*snapkit*"
Tuist/.build/checkouts/SnapKit
Tuist/.build/repositories/SnapKit-a5691e5b
```

Ya'ni `Tuist/Dependencies` papkasi **umuman yaratilmaydi**. Bu Tuist CLI manbasida ham ko'rinadi: `Constants.SwiftPackageManager.packageBuildDirectoryName = ".build"`. Shuning uchun `.gitignore` ga **`Tuist/.build`** ni qo'shing (eski misollardagi `Tuist/Dependencies` emas). `Tuist/Package.resolved` ni esa **commit qiling** — u butun jamoa uchun bir xil versiyalarni qotiradi.

```text
# .gitignore
Tuist/.build/
```

### 4.4 `.external(name:)` — paketni target'ga ulash

Endi istalgan target'dan paketga `TargetDependency.external` bilan murojaat qilasiz. Nom — paketning **product** nomi (odatda kutubxona nomi):

```swift
// Project.swift (RatesUI target'i)
.target(
    name: "RatesUI",
    destinations: .iOS,
    product: .framework,
    bundleId: "uz.azimjondev.depsdemo.ratesui",
    deploymentTargets: .iOS("17.0"),
    sources: ["RatesUI/Sources/**"],
    dependencies: [
        .target(name: "Core"),
        .external(name: "SnapKit"),   // ← tashqi paket
    ]
)
```

Kodda endi oddiy `import SnapKit` ishlaydi. `tuist generate` dan keyin Tuist tashqi paketni **alohida Xcode project** sifatida yaratadi va uni source'dan kompilyatsiya qiladi. Bizning generate natijamizda bu aniq ko'rindi:

```text
Generating workspace DepsDemo.xcworkspace
Generating project DepsDemo
Generating project SnapKit          ← SnapKit alohida project bo'ldi
Total time taken: 10.472s
```

Build paytida esa SnapKit haqiqatan source'dan kompilyatsiya qilindi (`Tuist/.build/checkouts/SnapKit/Sources/...`). Aynan shuning uchun XcodeProj-based integratsiya binary cache bilan ishlay oladi — paket "qora quti" emas, oddiy target.

> **💡 Pro tip:** Tashqi Swift Package project'lari uchun Tuist **scheme yaratmaydi** (scheme ro'yxati toza qolishi uchun). Agar sizga paketning o'z scheme'i kerak bo'lsa, uni Xcode UI orqali qo'lda yaratasiz.

### 4.5 Ketma-ketlik: install → generate → build

Amaliy ish oqimi doim shu tartibda:

```bash
tuist install     # Tuist/Package.swift ni o'qib, paketlarni Tuist/.build ga yuklaydi
tuist generate    # .xcworkspace + .xcodeproj yaratadi (tashqi paketlar alohida project)
# keyin build:
xcodebuild -workspace DepsDemo.xcworkspace -scheme DepsDemo \
  -destination 'generic/platform=iOS Simulator' build
```

Yangi paket qo'shsangiz yoki versiyani o'zgartirsangiz — `tuist install` ni qayta chaqirasiz. Faqat kod o'zgarsa — `install` kerak emas, to'g'ridan-to'g'ri build qilaverasiz.

![SPM paketni Tuist XcodeProj-based integratsiya bilan ulash oqimi: Tuist/Package.swift dan tuist install orqali Tuist/.build ga, keyin .external(name:) orqali alohida XcodeProj target'iga; yonida Xcode native yo'l](/lessons/images/tuist/tuist_04_spm_integration.svg)

Diagrammada chapdagi yo'l — tavsiya etilgan XcodeProj-based integratsiya (`tuist install` → `Tuist/.build` → `.external`), o'ngdagi — Xcode native yo'l (`Project(packages:)` → `.package(product:)`). Faqat chapdagi yo'l cache va selective testing bilan ishlaydi.

---

## 5. `PackageSettings` — paketlarni sozlash {#package-settings}

`PackageSettings` — `Tuist/Package.swift`ning `#if TUIST` blokida yoziladigan va tashqi paketlar XcodeProj target'lariga qanday aylanishini boshqaradigan tuzilma. Uning to'liq imzosi (manba: `PackageSettings.swift`, Tuist 4.211.1):

```swift
public init(
    productTypes: [String: Product] = [:],
    baseProductType: Product = .staticFramework,
    productDestinations: [String: Destinations] = [:],
    baseSettings: Settings = .settings(),
    expectedSignatures: [String: XCFrameworkSignature] = [:],
    targetSettings: [String: Settings] = [:],
    projectOptions: [String: Project.Options] = [:],
    includeLocalPackageTestTargets: Bool = false
)
```

Eng ko'p ishlatiladigan uchtasi:

**`productTypes`** — ma'lum paketning product type'ini o'zgartiradi. Default — `.staticFramework` (buni `baseProductType` boshqaradi). Masalan, Alamofire'ni dinamik qilish:

```swift
let packageSettings = PackageSettings(
    productTypes: [
        "Alamofire": .framework,       // dinamik framework
        "SnapKit": .staticFramework,   // statik (default)
    ]
)
```

**`baseSettings`** — paketlardan yaratilgan barcha target'larga qo'llanadigan build settings. Loyihangizda standart `Debug`/`Release` dan boshqa configuration bo'lsa, buni **shart** sozlaysiz — aks holda tashqi paketlar sizning configuration'laringizni bilmaydi va noto'g'ri build bo'ladi (manba: Tuist docs, issue #8345):

```swift
let packageSettings = PackageSettings(
    productTypes: [:],
    baseSettings: .settings(configurations: [
        .debug(name: "Base"),
        .release(name: "Production"),
    ])
)
```

**`targetSettings`** — alohida paket target'iga qo'shimcha build settings. Ko'pincha `-ObjC` yoki module-alias uchun ishlatiladi (11-bo'limda ko'ramiz).

**`expectedSignatures`** — SPM binary target'lari (masalan paket ichidagi `.binaryTarget`) uchun kutilgan imzolar. 3.3-bo'limdagi `XCFrameworkSignature` enum'i bilan ishlaydi:

```swift
let packageSettings = PackageSettings(
    expectedSignatures: [
        "SelfSignedXCFramework": .selfSigned(
            fingerprint: "EF61C3C0339FC84805357AFEC2E0BB0E6A0D5EE64165B333F934BF9E282785BC"
        ),
    ]
)
```

> **💡 Pro tip:** `includeLocalPackageTestTargets` (default `false`) — local path paketlarining test target'larini generatsiyaga kiritish-kiritmaslikni boshqaradi. Remote paketlarning testlari **hech qachon** kiritilmaydi. Katta loyihada buni `false` qoldirish generatsiyani yengillashtiradi.

---

## 6. `static` yoki `dynamic` — va "static side effect" {#static-dynamic}

Framework va kutubxonalar **statik** yoki **dinamik** link qilinishi mumkin. Tuist hujjati buni to'g'ridan-to'g'ri aytadi: bu tanlov ilova hajmi va ishga tushish (launch) vaqtiga **sezilarli ta'sir** qiladi, lekin ko'pincha o'ylamasdan tanlanadi.

Umumiy qoida (manba: Tuist docs — "Static or dynamic"):

- **Release build'da** iloji boricha ko'proq narsa **statik** link qilinsin → tez launch.
- **Debug build'da** iloji boricha ko'proq narsa **dinamik** link qilinsin → tez incremental build (iteratsiya).

|                       | Static (`.staticFramework`/`.staticLibrary`) | Dynamic (`.framework`)                         |
| --------------------- | -------------------------------------------- | ---------------------------------------------- |
| **App launch**        | Tezroq (bitta binary, dyld kam ish qiladi)   | Sekinroq (har bir framework alohida yuklanadi) |
| **Incremental build** | Sekinroq (ko'proq re-link)                   | Tezroq                                         |
| **Resurslar**         | ❌ Statik kutubxona resurs saqlay olmaydi    | ✅ Saqlaydi                                    |
| **Tuist default**     | ✅ (`.staticFramework`)                      |                                                |

> **🏢 Real-world:** Monzo jamoasi (Tuist case study, 2026-05-26 — ~50 dan ~200 modulgacha, 45+ iOS muhandis, 1.9M qator Swift, 20k unit + 8k snapshot test) aynan shu qoidani ishlatadi: **testlar dinamik framework** bilan quriladi (tez iteratsiya), **release esa statik** bilan (tez launch). Ularning module cache hit rate'i ~80% atrofida; cache hech qachon production build uchun ishlatilmaydi. Manba: `tuist/tuist` repozitoriyidagi Monzo case study.

### "Static side effect" — biz uchragan real ogohlantirish

Statik va dinamik link'ni grafikda almashtirish arzimas ish emas: o'zgarish butun grafikga kaskad ta'sir qiladi. Eng ko'p uchraydigan muammo — **bitta statik target bir nechta joydan link qilinishi**. Bu kodni ikki nusxalaydi (binary kattalashadi) yoki eng yomon holatda runtime crash'ga olib keladi.

Biz buni atayin yaratdik: `Core` ni `.staticFramework` qilib, uni ham `App`dan, ham `RatesUI`dan link qildik. `tuist generate` darhol ogohlantirdi (real natija):

```text
! Warning
  The following items may need attention:
   ▸ Target 'Core' has been linked from target 'DepsDemo' and target 'RatesUI',
     it is a static product so may introduce unwanted side effects.
```

**Nega?** `RatesUI` (dinamik framework) `Core` (statik) ni o'z ichiga oladi. `App` ham `Core`ni to'g'ridan-to'g'ri link qiladi. Natijada `Core`ning kodi **ikki joyda** — `RatesUI.framework` ichida va `App` binary'sida. Bu "duplicate symbols" yoki holat (static state) ikki nusxada bo'lishiga olib kelishi mumkin.

**Yechim:** yo `Core`ni `.framework` (dinamik) qiling (shunda bitta nusxa bo'ladi), yo `App` `Core`ni to'g'ridan-to'g'ri link qilmay, faqat `RatesUI` orqali transitive olsin. Qaysi biri to'g'riligini grafik va launch-vaqt talablari hal qiladi.

> **🧠 Esda tuting:** Tuist "qulaylik uchun implicit sozlash" qilmaydi (bu uning "cost of convenience" falsafasi). Ya'ni link type'ni va ba'zan kerak bo'ladigan qo'shimcha flag'larni (masalan `-ObjC`) **siz** to'g'ri qo'yasiz. Buning evaziga grafik aniq va bashorat qilinadigan bo'ladi.

### Linking type'ni environment orqali boshqarish

Tuist statik↔dinamik murakkabligini "siqadi" va bundle accessor'larni har ikki holat uchun bir xil qilib synthesize qiladi. Shuning uchun linking type'ni chaqiruv vaqtida environment orqali uzatish mumkin (manba: Tuist docs):

```swift
// Manifest ichida: ENV orqali kelgan qiymatga qarab product type tanlash
func productType() -> Product {
    if case let .string(linking) = Environment.linking {
        return linking == "static" ? .staticFramework : .framework
    }
    return .framework
}
```

Keyin `TUIST_LINKING=static tuist generate` kabi chaqirasiz. Bu bitta manifestdan ham debug (dinamik), ham release (statik) grafigini olish imkonini beradi.

---

## 7. Swift macro'lar — tashqi va o'zingniki {#macros}

Swift macro — kompilyator plagini: Swift kompilyatori compile vaqtida yuklab ishga tushiradigan executable. Tuist macro'larni ikki stsenariyda qo'llab-quvvatlaydi (manba: Tuist docs — "Swift Macros"), va qaysi birida ekaningizni aniq bilish muhim.

### 7.1 Tashqi paketdagi macro'ni ishlatish

Ko'pincha siz boshqa birovning macro'sini ishlatasiz (masalan `@Reducer` — The Composable Architecture'dan, yoki `@Builder` — StructBuilder'dan). Ikkala integratsiya ham buni qo'llaydi:

**XcodeProj-based (tavsiya etilgan):** paketni `Tuist/Package.swift`da e'lon qilasiz va `.external(name:)` bilan bog'lanasiz — Tuist macro product'ini native target'ga xaritalaydi. Bu Tuist'ning rasmiy fixture'ida aynan shunday (manba: `generated_framework_with_native_swift_macro`):

```swift
// Project.swift
.target(
    name: "Framework",
    destinations: [.iPhone, .mac],
    product: .staticLibrary,
    bundleId: "dev.tuist.FrameworkWithSwiftMacro",
    sources: ["Sources/**/*"],
    dependencies: [
        .external(name: "ComposableArchitecture"),
        .external(name: "CasePaths"),
        .external(name: "StructBuilder"),   // macro shu paket ichida
    ]
)
```

**Xcode native:** `Project(packages:)` + `.package(product:type: .macro)`. `PackageType`ning `.macro` holati aynan shu uchun (manba: `TargetDependency.swift`):

```swift
let project = Project(
    name: "FrameworkWithSwiftMacro",
    packages: [
        .remote(url: "https://github.com/alschmut/StructBuilderMacro", requirement: .exact("0.2.0")),
    ],
    targets: [
        .target(
            name: "Framework",
            destinations: .iOS,
            product: .framework,
            bundleId: "dev.tuist.FrameworkWithSwiftMacro",
            sources: ["Sources/**/*"],
            dependencies: [
                .package(product: "StructBuilder", type: .macro),
            ]
        ),
    ]
)
```

> **🧠 Esda tuting:** Build-tool plugin'lardan (`.plugin`) farqli o'laroq, macro'lar faqat Xcode native yo'l bilan cheklanmagan — ularni XcodeProj-based integratsiya bilan ham ishlatsa bo'ladi. Build-tool plugin'lar esa **faqat** Xcode native yo'l bilan ishlaydi (masalan SwiftLint'ni build phase'da ishga tushirish).

### 7.2 O'z macro'ngizni yaratish

Tuist'ning `.macro` degan **first-class product type**'i bor (manba: `Product.swift`da `case macro`). Xcode o'zi "Swift Macro target" turini bermaydi — Tuist buni command-line tool target yaratib, uni macro ishlatuvchi target'larga dependency qilib ulab, Swift kompilyator sozlamalariga `-load-plugin-executable` flag'ini inject qilib amalga oshiradi. Bularning hammasini Tuist o'zi qiladi.

Macro odatda **ikki target**ga bo'linadi:

1. **Plugin** target (`product: .macro`) — macro'ni amalga oshiradigan executable. U build mashinasida ishlagani uchun **faqat macOS** (host-only).
2. **Library** target — macro'ning public interfeysini (`@attached`/`@freestanding` e'lonlarini) beradi va plugin'ga `.macro(name:)` bilan bog'lanadi.

Macro implementatsiyasi [swift-syntax](https://github.com/swiftlang/swift-syntax)ga bog'liq — uni `Tuist/Package.swift`da oddiy tashqi dependency sifatida e'lon qilasiz:

```swift
// Tuist/Package.swift
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "PackageName",
    dependencies: [
        .package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.0"),
    ]
)
```

Keyin target'larni to'g'ridan-to'g'ri `Project.swift`da e'lon qilasiz (manba: Tuist docs — "Defining your own macro"):

```swift
// Project.swift
let project = Project(
    name: "MyLibrary",
    targets: [
        // Macro'ni amalga oshiruvchi plugin — macOS-only, compile vaqtida ishlaydi
        .target(
            name: "MyMacrosImplementation",
            destinations: [.mac],
            product: .macro,
            bundleId: "dev.tuist.MyMacrosImplementation",
            sources: ["Sources/MyMacrosImplementation/**"],
            dependencies: [
                .external(name: "SwiftSyntaxMacros"),
                .external(name: "SwiftCompilerPlugin"),
            ]
        ),
        // Macro e'lonlarini beruvchi library — plugin'ga .macro(name:) bilan bog'lanadi
        .target(
            name: "MyLibrary",
            destinations: .iOS,
            product: .framework,
            bundleId: "dev.tuist.MyLibrary",
            sources: ["Sources/MyLibrary/**"],
            dependencies: [
                .macro(name: "MyMacrosImplementation"),
            ]
        ),
    ]
)
```

`MyLibrary`ni link qilgan har qanday target endi macro'ni ishlatadi — Tuist plugin'ni propagatsiya qiladi, kompilyator uni yuklaydi.

> **⚠️ Diqqat:** Biz bu macro misolini `tuist generate` gacha olib bormadik, chunki swift-syntax 600.x ni noldan kompilyatsiya qilish og'ir (bir necha daqiqa) va bu mashinada u cache'da yo'q edi. Yuqoridagi manifest naqshlari Tuist'ning rasmiy hujjati, API manbasi (`Product.swift`, `TargetDependency.swift`) va ishlaydigan fixture'lari (`generated_framework_with_macros_and_tests`) bilan tasdiqlangan. Fixture'da library plugin'ga `.target(name:)` bilan ham bog'lanadi; hujjat esa maxsus `.macro(name:)` holatini tavsiya qiladi — ikkalasi ham ishlaydi, `.macro(name:)` aniqroq.

---

## 8. `tuist graph` — grafikni ko'rish {#graph}

`tuist graph` loyihaning dependency grafigini tasvir yoki matn ko'rinishida chiqaradi. Katta loyihada bu "qaysi modul qaysisiga bog'liq?" degan savolga bir qarashda javob beradi.

Default'da u PNG yaratib ochadi. Skript yoki CI uchun matn (`dot`/`json`) formatini `--no-open` bilan birga ishlatgan ma'qul. Flaglar (manba: `tuist graph --help`, 4.211.1):

```bash
tuist graph --format dot --no-open -o graphout        # dot matni graphout/graph.dot ga
tuist graph --format json --no-open -o graphout       # JSON
tuist graph -t --skip-external-dependencies ...        # test target'lar va tashqi paketlarsiz
tuist graph App                                        # faqat App va unga bog'liqlar
```

> **⚠️ Diqqat:** `-o` — chiqish **papkasi**, fayl nomi emas. Biz boshida `-o graph.dot` deb fayl nomi berganimizda real xato oldik: `✖ Error — Parent directory not found: .../graph.dot`. To'g'risi — mavjud papkani berish: `-o graphout` (ichida `graph.dot` yaratiladi).

Bizning `DepsDemo` loyihamizdan olingan real `dot` natija (faqat ichki target'lar):

```text
digraph {
  Core [fillcolor=coral1 shape=cylinder style=filled]
  DepsDemo [fillcolor=deepskyblue penwidth="1.5" shape=box3d style=filled]
  RatesUI [fillcolor=darkgoldenrod1 shape=cylinder style=filled]
  DepsDemo -> RatesUI
  DepsDemo -> Core
  RatesUI -> Core
}
```

Tashqi paketlar va system framework'lar bilan to'liq grafik (`--no-skip-external-dependencies`):

```text
digraph {
  AVFoundation [fillcolor=violet shape=rectangle style=filled]
  Core [fillcolor=coral1 shape=cylinder style=filled]
  DepsDemo [fillcolor=deepskyblue shape=box3d style=filled]
  RatesUI [fillcolor=darkgoldenrod1 shape=cylinder style=filled]
  SnapKit [fillcolor=coral1 shape=cylinder style=filled]
  DepsDemo -> RatesUI
  DepsDemo -> AVFoundation
  DepsDemo -> Core
  RatesUI -> Core
  RatesUI -> SnapKit
}
```

E'tibor bering: `DepsDemo -> Core` **va** `RatesUI -> Core` — ya'ni grafik 6-bo'limdagi "static side effect"ni ham ko'rsatib turibdi (`Core` ikki joydan kelmoqda). Grafik — nafaqat chiroyli rasm, balki muammoni ko'rsatuvchi vosita.

> **💡 Pro tip:** Macro grafikini ko'rayotganda `--skip-macro-support-targets` (`-m`) SwiftSyntax/SwiftCompilerPlugin kabi yordamchi target'larni yashiradi va asosiy grafikni toza ko'rsatadi. `--format svg` ni esa to'g'ridan-to'g'ri hujjatlarga qo'ysa bo'ladi.

---

## 9. `tuist inspect dependencies` — implicit import'ni tutish {#inspect}

Bu — Tuist'ning eng muhim "nazorat" buyrug'i va 1-bo'limdagi "aniq grafik" falsafasining amaliy quroli.

**Muammo:** Xcode build system dependency'larni **implicit** (yashirin) aniqlashga ruxsat beradi. Har qanday dasturchi kodga `import SomeFramework` yozsa, implicit bog'lanish paydo bo'ladi — garchi u `Project.swift`da e'lon qilinmagan bo'lsa ham. Kichik loyihada bu muammo emas, lekin grafik o'sgani sari ishonchsiz incremental build'lar, buzilgan preview'lar va kod-to'ldirish (completion) muammolariga aylanadi. Eng yomoni — binary cache yoqilganda: Tuist statik tahlil bilan topa olmagan bog'lanish "missing symbol" xatosiga olib keladi.

**Yechim:** `tuist inspect dependencies` kodni statik tahlil qilib, e'lon qilinmagan (implicit) va keraksiz (redundant) import'larni topadi. Topsa — **noldan farqli exit code** bilan to'xtaydi, demak CI'da ushlab qolasiz.

```bash
tuist inspect dependencies                   # ikkalasini ham: implicit + redundant
tuist inspect dependencies --only implicit   # faqat implicit
tuist inspect dependencies --only redundant  # faqat redundant (e'lon qilingan, lekin import qilinmagan)
```

### Real demo: implicit import'ni atayin yaratamiz

Bizning `RatesUI` target'i kodda `import Core` qiladi. Endi `Project.swift`dan uning `.target(name: "Core")` bog'lanishini **atayin olib tashlaymiz** (kodga tegmasdan):

```swift
.target(
    name: "RatesUI",
    // ...
    dependencies: [
        // .target(name: "Core")  ← ATAYLAB o'chirdik, lekin kodda `import Core` bor
        .external(name: "SnapKit"),
    ]
),
```

`tuist generate` **o'tadi** (generatsiya import'larni tekshirmaydi — faqat cycle'ni). Lekin tekshiruvni ishga tushirsak, real natija:

```text
$ tuist inspect dependencies --only implicit
Loading and constructing the graph
✖ Error
  The following implicit dependencies were found:
   - RatesUI implicitly depends on: Core

$ echo $?
1
```

Exit code **1** — aynan shuning uchun buni CI'ga qo'yish mumkin. Tuzatsak (bog'lanishni qaytarsak) — toza o'tadi:

```text
$ tuist inspect dependencies --only implicit
We did not find any dependency issues in your project (checked: implicit).
$ echo $?
0
```

![Implicit import tekshiruvi mexanizmi: kodda import Core bor, lekin grafikda RatesUI dan Core ga qirra yo'q; tuist inspect dependencies statik tahlil qiladi va exit code 1 bilan to'xtaydi](/lessons/images/tuist/tuist_04_implicit_check.svg)

Diagramma mexanizmni ko'rsatadi: chapda kodda `import Core` bor, lekin grafikda `RatesUI → Core` qirrasi yo'q (implicit). O'ngda `tuist inspect dependencies` statik tahlildan so'ng exit code 1 qaytaradi — CI shu yerda to'xtaydi.

> **💡 Pro tip:** 2025-12-13 dan beri `tuist inspect dependencies` ikkala tekshiruvni (implicit + redundant) birga ishga tushiradi va grafikni bir marta yuklagani uchun ~2 barobar tezroq. Eski `tuist inspect implicit-imports` buyrug'i hali ishlaydi, lekin deprecated — biz uni ishga tushirganimizda aniq ogohlantirdi: `The 'tuist inspect implicit-imports' command is deprecated. Use 'tuist inspect dependencies --only implicit' instead.` Shuningdek u `tuist inspect --help` ro'yxatida ko'rinmaydi.

> **⚠️ Diqqat:** Tuist statik tahlilga tayangani uchun **hamma holatni tuta olmaydi**. Masalan, compiler directive (`#if ...`) ichidagi shartli import'larni tushunmaydi. Shuning uchun bu — ishonchli, lekin 100% emas, ikkinchi himoya qatlami.

> **🚀 Motivatsiya:** Toss aynan shu tekshiruvni PR pipeline'iga qo'ygan: ularning ~1,000 modulli loyihasida cacheable-modul ulushi tushib ketsa (ya'ni implicit bog'lanishlar ko'paysa), PR tekshiruvi ogohlantiradi. Bu binary cache'ni ishchi holatda saqlaydi — chunki explicit modules va CAS faqat implicit bog'lanishlar bo'lmaganda ishlaydi (manba: Toss case study, 2026-10).

---

## 10. Swift Package Registry — resolve'ni tezlashtirish {#registry}

Dependency'lar soni ortgani sari ularni resolve qilish vaqti ham ortadi. SwiftPM markazlashmagan (decentralized): u har bir paketni **chuqur klonlash** (deep clone) orqali resolve qiladi — bu sekin va ko'p xotira oladi. Tuist buni [Swift Package Registry](https://github.com/swiftlang/swift-package-manager/blob/main/Documentation/PackageRegistry/PackageRegistryUsage.md) implementatsiyasi bilan hal qiladi: butun repozitoriyni emas, faqat **kerakli commit'larni** yuklaysiz. Registry'dagi paketlar [Swift Package Index](https://swiftpackageindex.com/) asosida — agar paket o'sha yerda bo'lsa, Tuist Registry'da ham bor (manba: Tuist docs — "Swift Package Registry").

Ikki xil o'rnatish bor:

**1) Generatsiya paytida avtomatik (tavsiya etilgan):** `Tuist.swift`da yoqasiz:

```swift
// Tuist.swift
import ProjectDescription

let tuist = Tuist(
    project: .tuist(
        generationOptions: .options(
            registryEnabled: true   // (manba: ConfigGenerationOptions.swift)
        )
    )
)
```

Shunda `tuist generate` registry konfiguratsiya faylini avtomatik yaratadi — alohida `tuist registry setup` kerak emas.

**2) Qo'lda:**

```bash
tuist registry setup    # registry konfiguratsiya faylini yaratadi (commit qiling)
```

### Registry identifikatorlari

URL o'rniga registry `{organization}.{repository}` ko'rinishidagi identifikatordan foydalanadi. Masalan:

- `https://github.com/pointfreeco/swift-composable-architecture` → `pointfreeco.swift-composable-architecture`
- `https://github.com/groue/GRDB.swift` → `groue.GRDB_swift` (nuqta bittadan ko'p bo'lmasligi kerak, shuning uchun `.` → `_`)

### Autentifikatsiya va limitlar

Autentifikatsiya **ixtiyoriy**:

- **Hisobsiz:** IP manzil uchun daqiqasiga **1,000 so'rov** limiti.
- **Hisob bilan** (`tuist registry login`): daqiqasiga **20,000 so'rov**. Buning uchun Tuist hisobi va project kerak.

> **⚠️ Diqqat:** Registry — Tuist serveriga tayanadigan xizmat. Asosiy foyda (tez resolve) hisobsiz ham ishlaydi, lekin yuqori limit va jamoaviy ishlatish uchun Tuist hisobi kerak. Biz bu bo'limni mashinada ishga tushirmadik (hisob/tarmoq xizmati talab qiladi) — ma'lumotlar Tuist rasmiy hujjati va `ConfigGenerationOptions.swift` manbasiga asoslangan.

> **📊 Real raqamlar (vendor):** SwiftPM resolve "daqiqalardan soniyalarga" tushadi — bu Tuist'ning o'z da'vosi (registry sahifasi meta-tavsifi). Neytral, uchinchi tomon o'lchovi yo'q, shuning uchun uni vendor raqami sifatida qabul qiling.

---

## 11. Troubleshooting — `-ObjC`, Firebase, TCA, CocoaPods {#troubleshooting}

Tashqi paketlar bilan eng ko'p uchraydigan real muammolar va ularning yechimi (hammasi Tuist'ning o'z hujjatidan — "Troubleshooting").

### 11.1 Objective-C paketlar va `-ObjC`

Tuist **implicit linker config qo'ymaydi.** Objective-C paketlari runtime crash'dan qochish uchun `-ObjC` flag'ini talab qilishi mumkin (Apple Technical Q&A QA1490). SwiftPM `-ObjC`ni `.unsafeFlag` deb hisoblagani uchun ko'p paket uni o'zi qo'sha olmaydi. Yechim — iste'mol qiluvchi target'da `OTHER_LDFLAGS`ni qo'yish:

```swift
.target(
    // ...
    settings: .settings(
        base: ["OTHER_LDFLAGS": "$(inherited) -ObjC"]
    )
)
```

### 11.2 Firebase va Google kutubxonalari

Google'ning open-source kutubxonalari ko'pincha nostandart arxitektura ishlatadi. Ikki tipik muammo:

1. **`-ObjC` kerak** — ko'p Google kutubxonasi Objective-C'da (yuqoridagi 11.1).
2. **`FBLPromises` crash** — ba'zi Google kutubxonalari `FBLPromises`ga bog'liq. Quyidagidek crash ko'rsangiz:

```text
NSInvalidArgumentException. Reason: -[FBLPromise HTTPBody]:
unrecognized selector sent to instance 0x600000cb2640.
```

yechim — `FBLPromises`ni dinamik framework qilish:

```swift
let packageSettings = PackageSettings(
    productTypes: [
        "FBLPromises": .framework,
    ]
)
```

> **🧠 Esda tuting:** `FBLPromises`ni `.framework` qilish — **ma'lum bir crash**ning yechimi, har doim shart emas. Avval oddiy holda sinab ko'ring, crash bo'lsa shu sozlamani qo'shing.

### 11.3 The Composable Architecture (TCA) va previews

TCA statik link qilinganda (Tuist default) app va test target'lari odatda ishlaydi, lekin **SwiftUI preview'lar buziladi.** Yechim — hamma narsani dinamik link qilish. Tuist hujjati buning uchun to'liq `productTypes` ro'yxatini beradi (CasePaths, ComposableArchitecture, Dependencies, Perception, Sharing...) va `Sharing` paketi uchun module-alias kerakligini ko'rsatadi:

```swift
let packageSettings = PackageSettings(
    productTypes: [
        "ComposableArchitecture": .framework,
        "CasePaths": .framework,
        "Dependencies": .framework,
        // ... (hujjatdagi to'liq ro'yxat)
        "Sharing": .framework,
    ],
    targetSettings: [
        "Sharing": .settings(base: [
            "PRODUCT_NAME": "SwiftSharing",
            "OTHER_SWIFT_FLAGS": ["-module-alias", "Sharing=SwiftSharing"],
        ]),
    ]
)
```

> **⚠️ Diqqat:** Bunda kodda `import Sharing` emas, `import SwiftSharing` yozasiz. Dinamik link doim tavsiya etilmaydi — bu yerda soddalik uchun hammasi dinamik qilingan.

### 11.4 Statik symbol'lar `.swiftmodule` orqali "sizib chiqishi"

Dinamik framework statik modulga `import StaticModule` orqali bog'lansa, symbol'lar dinamik framework'ning `.swiftmodule`iga kirib, build'ni buzishi mumkin. Swift 6'da yechim — `internal import`:

```swift
internal import StaticModule
```

Eski Swift versiyalarida `@_implementationOnly import StaticModule` ishlatiladi.

### 11.5 CocoaPods va Carthage

- **Carthage:** `carthage update` → `.framework`/`.xcframework` chiqaradi → `.framework`/`.xcframework` dependency turi bilan ulaysiz. Build/test'dan oldin `carthage update` chaqirilgan bo'lishi kerak.
- **CocoaPods:** `tuist generate` → keyin `pod install`. Lekin CocoaPods dependency'lari **binary cache va selective testing bilan mos emas** (fingerprint logikasi Pod'larni hisobga olmaydi), shuningdek generatsiyadan keyin darhol `xcodebuild` ishlatuvchi `build`/`test` oqimlari bilan ham mos emas.

> **🏢 Real-world:** Asana jamoasi (~10 iOS muhandis, ~300k qator Swift, 2023-yil blog) Bazel o'rniga Tuist'ni tanladi — sabab: Bazel "juda og'ir", "yangi mental model talab qiladi" va "breakpoint muammolari" bor edi. µFeature'larga bo'lgandan keyin o'zgartir-qayta-qur sikli mediana 45 soniyadan ~15 soniyaga, Inbox modulidagi bir qatorlik o'zgarish esa 30–50 soniyadan ~4 soniyaga tushdi (Asana'ning o'z log'lari). Narx: git checkout 5–15 s sekinlashdi, ~3 oy migratsiya, minglab symbol skript bilan `public` qilindi. Manba: asana.com inside-asana blogi.

---

## Amaliy mashqlar {#mashqlar}

Hammasini vaqtinchalik papkada bajaring (repozitoriyga tegmang). Har bir mashqdan keyin `tuist graph` bilan natijani ko'ring.

1. **Ikki modulli loyiha.** `Core` (`.staticFramework`) va `Feature` (`.framework`) target'li `Project.swift` yozing. `Feature` `Core`ga `.target(name:)` bilan bog'lansin va kodda `MoneyFormatter`ga o'xshash public funksiyani ishlatsin. `tuist generate && tuist graph --format dot --no-open -o out` bilan `Feature -> Core` qirrasini tasdiqlang.

2. **Tashqi paket qo'shish.** `Tuist/Package.swift`ga bitta kichik paket qo'shing (masalan SnapKit). `tuist install` ni ishga tushiring va `ls -a Tuist/` bilan paket **`Tuist/.build`**ga tushganini (`Tuist/Dependencies` emas) tasdiqlang. Keyin uni `.external(name:)` bilan `Feature`ga ulab, `import` qilib, `xcodebuild ... build` bilan `BUILD SUCCEEDED` oling. `.gitignore`ga `Tuist/.build/` qo'shing.

3. **⭐ Implicit import'ni tuting.** `Feature` kodida `import Core` bo'la turib, `Project.swift`dan `.target(name: "Core")`ni olib tashlang. `tuist generate` o'tadi. Keyin `tuist inspect dependencies --only implicit` ishga tushiring va `echo $?` bilan exit code **1** ekanini ko'ring. Bog'lanishni qaytaring — endi exit code **0** va "no dependency issues" bo'lsin.

4. **⭐ "Static side effect"ni yuzaga keltiring va tuzating.** `Core`ni `.staticFramework` qoldirib, uni ikki target'dan (masalan `App` va `Feature`) link qiling. `tuist generate` ogohlantirishini o'qing. So'ng ikki yechimdan birini qo'llang: `Core`ni `.framework` qiling **yoki** `App` dan to'g'ridan-to'g'ri bog'lanishni olib tashlang. Ogohlantirish yo'qolganini tasdiqlang.

5. **⭐ `status` va `condition`.** Bitta `.sdk` dependency'ni `.optional` qiling (`.sdk(name:type:status: .optional)`) va bitta `.target`ni `condition: .when([.ios])` bilan shartli qiling. `tuist generate` o'tishini va grafik to'g'ri ekanini tekshiring.

6. **⭐⭐ O'z macro'ngizning skeleti.** 7.2-bo'limdagi naqsh asosida `MyMacrosImplementation` (`product: .macro`, `.mac`) va `MyLibrary` (`.macro(name:)` bilan bog'langan) target'larini yozing. `Tuist/Package.swift`ga swift-syntax qo'shing. (Diqqat: `tuist install` swift-syntax'ni yuklab, kompilyatsiya qiladi — bu bir necha daqiqa olishi mumkin.)

## O'zingizni tekshiring {#checklist}

- [ ] `.target`, `.project`, `.external` va `.sdk` orasidagi farqni va har biri qachon kerakligini aytib bera olaman.
- [ ] Xcode native va Tuist XcodeProj-based integratsiya farqini bilaman va nega `.external` cache bilan ishlashini tushunaman.
- [ ] `tuist install` paketlarni **`Tuist/.build`**ga yuklashini bilaman va uni `.gitignore` qilaman, `Package.resolved`ni esa commit qilaman.
- [ ] `.staticFramework` va `.framework` farqini, va "static side effect" ogohlantirishi nimani anglatishini bilaman.
- [ ] Implicit import nima ekanini va uni `tuist inspect dependencies --only implicit` qanday tutishini (exit code 1) bilaman.
- [ ] `tuist graph` bilan dot/json chiqara olaman va `-o` papka ekanini eslayman.
- [ ] Macro'ni tashqaridan ishlatish va o'zim yaratishni farqlay olaman (`.external` / `.package(.macro)` vs `.macro` product + `.macro(name:)`).
- [ ] `-ObjC`, Firebase/`FBLPromises`, TCA/previews muammolarining yechimini bilaman.

## Xulosa {#xulosa}

- Dependency graph **aniq (explicit)** va **statik** bo'lishi kerak — faqat shundagina tekshiriladi va optimallashadi. Tuist buni generatsiya paytida validatsiya qiladi.
- `TargetDependency`ning 10 holati bor: local (`.target`, `.project`), binary (`.framework`, `.library`, `.xcframework`), system (`.sdk`, `.xctest`), tashqi (`.external`, `.package`, `.macro`). Har biri `status` va `condition` oladi.
- Tashqi Swift Package'lar uchun tavsiya — **XcodeProj-based integratsiya** (`Tuist/Package.swift` + `.external(name:)`): u binary cache va selective testing bilan ishlaydi.
- `tuist install` paketlarni **`Tuist/.build`**ga yuklaydi (rasmiy hujjatdagi `Tuist/Dependencies` eskirgan — biz tekshirdik). `Package.resolved` — commit qilinadi, `.build` — gitignore.
- `.staticFramework` default; statik target'ni bir nechta joydan link qilish "static side effect" ogohlantirishini beradi — buni jiddiy qabul qiling.
- `tuist inspect dependencies --only implicit` — grafik tozaligini CI'da nazorat qiluvchi asosiy quroldir (implicit topilsa exit code 1). Toss buni ~1,000 modulda cache'ni ishchi saqlash uchun ishlatadi.
- Swift Package Registry resolve'ni tezlashtiradi (Swift Package Index asosida); yuqori limit uchun Tuist hisobi kerak.
- **Keyingi dars:** [05. Migratsiya — Xcode (va XcodeGen/SPM/Bazel) loyihasidan Tuist'ga bosqichma-bosqich o'tish](/ios-lessons/tuist-05-migration)

## Manbalar {#manbalar}

1. [Tuist Docs — Dependencies](https://tuist.dev/en/docs/guides/features/projects/dependencies) — local/external dependency turlari, PackageSettings, static/dynamic, troubleshooting (FBLPromises, TCA, `-ObjC`).
2. [Tuist Docs — Implicit imports (inspect dependencies)](https://tuist.dev/en/docs/guides/features/projects/inspect/implicit-dependencies) — implicit import nazorati va CI tavsiyasi.
3. [Tuist changelog — inspect dependencies (2025-12-13)](https://tuist.dev/changelog/2025.12.13-inspect-dependencies) — birlashtirilgan implicit+redundant tekshiruv.
4. [Tuist Docs — Swift Package Registry](https://tuist.dev/en/docs/guides/features/package-registries/swift) — registry identifikatorlari, limitlar, `registryEnabled`.
5. [Tuist Docs — v3 → v4 migration](https://tuist.dev/en/docs/references/migrations/from-v3-to-v4) — `fetch`→`install`, `Dependencies.swift`→`Package.swift` o'zgarishlari.
6. [Monzo case study (Tuist, 2026-05-26)](https://github.com/tuist/tuist/blob/main/server/priv/marketing/case_studies/monzo.md) — testlar dinamik, release statik; ~200 modul, ~80% cache hit.
7. [Toss case study (Tuist, 2026-10)](https://github.com/tuist/tuist/blob/main/server/priv/marketing/case_studies/toss.md) va [Tuist "Scaling" blogi (2025-09-22)](https://tuist.dev/blog/2025/09/22/scale) — grafikni cacheable qilish, implicit import nazorati.
8. [Asana — Scaling a mature iOS codebase with Tuist](https://asana.com/inside-asana/scaling-a-mature-ios-codebase-with-tuist) — Bazel o'rniga Tuist tanlovi, real build raqamlari.
9. [Apple Technical Q&A QA1490 — `-ObjC`/`-force_load`](https://developer.apple.com/library/archive/qa/qa1490/_index.html) — Objective-C kategoriyalari uchun linker flag.
10. [The Composable Architecture — static linking muhokamasi](https://github.com/pointfreeco/swift-composable-architecture/discussions/1657) — `-ObjC` va previews muammosi.
11. [swift-syntax](https://github.com/swiftlang/swift-syntax) — macro implementatsiyasi bog'liqligi.
12. [Swift Package Index](https://swiftpackageindex.com/) — Tuist Registry paketlari manbasi.
13. [SnapKit](https://github.com/SnapKit/SnapKit) — darsdagi tekshirilgan tashqi paket (5.7.1).
