# Tuist 02: Manifest fayllar — Project.swift, Workspace.swift, Tuist.swift va Package.swift (2026 API)

> **Holat:** 2026-yil oktabr. Bu dars Tuist loyihasini belgilaydigan to'rtta manifest faylni — `Project.swift`, `Target`, `Workspace.swift`, `Tuist.swift` va `Tuist/Package.swift` — 2026-yilgi (Tuist 4.211.1) API bo'yicha noldan ekspert darajasigacha o'rgatadi. Hamma kod misollari Tuist 4.211.1 va Xcode 27.1 bilan Apple M4 Pro'da (12 core, 24 GB) tekshirilgan.

> **Bu darsdan keyin siz:** bo'sh papkadan boshlab, bir nechta modul, ikkita configuration, xcconfig, custom scheme va resource'lari bo'lgan loyihani faqat Swift kodi bilan ta'riflab, uni generate qilib, build va test qila olasiz.

- `Project.swift` ichidagi har bir maydonni (`name`, `options`, `settings`, `targets`, `schemes`, ...) tushunasiz va nima uchun kerakligini bilasiz.
- `Target` ning 26 ta parametri, ayniqsa `destinations` vs `deploymentTargets`, `product` turlari va `buildableFolders` vs `sources`/`resources` farqini amalda ishlatasiz.
- `Tuist.swift` ning **2026-yilgi yangi** `Tuist(project: .tuist(...))` shaklini yozasiz va eskirgan (`deprecated`) API'larni taniysiz.
- `Workspace.swift` bilan ko'p loyihali workspace quryapsiz, `Tuist/Package.swift` bilan tashqi paketlarni ulash sintaksisini bilasiz.
- `tuist edit`, `tuist dump` va manifest caching qanday ishlashini, implicit dependency'ni `tuist inspect dependencies` bilan qanday ushlashni ko'rasiz.

## Mundarija

1. [Manifest nima va nega Swift tilida?](#nega-swift)
2. [ProjectDescription va to'rtta manifest fayl](#tort-manifest)
3. [Project.swift — loyihaning yuragi](#project-swift)
4. [Target anatomiyasi — bitta build mahsuloti](#target-anatomiyasi)
5. [buildableFolders vs sources/resources globs (2026 yangiligi)](#buildable-folders)
6. [Settings, configuration va xcconfig](#settings)
7. [Scheme'lar — avtomatik va custom](#schemes)
8. [Tuist.swift — 2026 global konfiguratsiya](#tuist-swift)
9. [Workspace.swift — ko'p loyihali workspace](#workspace-swift)
10. [Tuist/Package.swift va PackageSettings](#package-swift)
11. [tuist edit, tuist dump va manifest caching](#edit-dump-cache)
12. [To'liq misol: noldan generate → build → test](#toliq-misol)
13. [Amaliy mashqlar](#mashqlar)
14. [O'zingizni tekshiring](#checklist)
15. [Xulosa](#xulosa)
16. [Manbalar](#manbalar)

---

## 1. Manifest nima va nega Swift tilida? {#nega-swift}

Xcode loyihasini oddiy usulda yaratganingizda, hamma narsa `MyApp.xcodeproj` ichidagi `project.pbxproj` faylga yoziladi. Bu fayl — mashina uchun yozilgan, OpenStep plist formatidagi, yuzlab UUID'lardan iborat matn. Uni odam qo'li bilan tahrirlab bo'lmaydi, va eng yomoni: ikki dasturchi bir vaqtda target yoki fayl qo'shsa, Git'da deyarli har safar **merge conflict** chiqadi.

Tuist bu muammoni boshqa tomondan hal qiladi: siz `.pbxproj` ni umuman tahrirlamaysiz. Uning o'rniga loyihani **Swift kodida ta'riflaysiz**, Tuist esa `.xcodeproj` ni har safar yangidan **generate** qiladi. Loyihani ta'riflaydigan bu Swift fayllar **manifest fayllar** deb ataladi.

Bu bo'yicha Tuist hujjati aniq aytadi: Swift tanlanishiga Swift Package Manager (`Package.swift`) ilhom bergan. Swift ishlatilgani uchun kompilyator manifest to'g'riligini tekshiradi, fayllar orasida kodni qayta ishlatish mumkin, Xcode esa syntax highlighting, autocomplete va validatsiya beradi.

> **🧠 Esda tuting:** Manifest — bu Xcode loyihangizning **manbasi** (source of truth). `.xcodeproj` esa — kompilyatsiya mahsuloti kabi: generate qilinadigan, Git'ga qo'shilmaydigan natija. Shuning uchun `.xcodeproj` / `.xcworkspace` odatda `.gitignore` ga tushadi.

Manifestlar Swift bo'lgani uchun **kompilyatsiya qilinadi**. Shuning uchun Tuist ularni birinchi marta yurgizganda sal sekinroq, keyingi safar esa tezroq ishlaydi — kompilyatsiya natijasini u cache'laydi. Bu haqda 11-bo'limda aniq raqamlar bilan ko'ramiz.

> **🚀 Motivatsiya:** `.pbxproj` merge conflict'lari jamoangizning eng behuda vaqt yo'qotishlaridan biri. Manifestga o'tganingizda bu conflict'lar deyarli nolga tushadi, chunki har kim o'zgartiradigan narsa — o'qiladigan, diff bo'ladigan, review qilinadigan oddiy Swift kodi. Asana muhandislik jamoasi (asana.com, 2023-yil blog) o'z iOS kod bazasini Tuist'ga ko'chirganda, ~1,400 qatorlik bitta manifest yozib, keyin kodni modullarga ajratgan — natijada feature'ni o'zgartirib qayta build qilish vaqti ularning o'lchoviga ko'ra 45 soniyadan 15 soniyaga tushgan.

---

## 2. ProjectDescription va to'rtta manifest fayl {#tort-manifest}

Har bir manifest fayl eng yuqorida `import ProjectDescription` bilan boshlanadi. **ProjectDescription** — bu Tuist bilan birga keladigan Swift framework; `Project`, `Target`, `Workspace`, `Tuist`, `Settings`, `Scheme` kabi hamma turlar shu yerda. Ya'ni manifest yozganingizda siz aslida shu framework'ning API'sidan foydalanasiz.

2026-yilda bir Tuist loyihasida siz asosan **to'rtta** manifest fayl bilan ishlaysiz:

| Fayl                  | Vazifasi                                                                                   | Majburiymi?                         |
| --------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------- |
| `Tuist.swift`         | Loyiha **root**'ini belgilaydi + global konfiguratsiya (server, cache, generation options) | Tavsiya etiladi (root'ni aniqlaydi) |
| `Project.swift`       | Bitta Xcode **project**'ni ta'riflaydi: target'lar, settings, scheme'lar                   | Ha (loyihada kamida bitta)          |
| `Workspace.swift`     | Bir nechta project'ni bitta **workspace**'ga yig'adi                                       | Yo'q (faqat ko'p loyihada)          |
| `Tuist/Package.swift` | Tashqi **SPM paketlari**ni (dependency) e'lon qiladi                                       | Yo'q (faqat tashqi paket bo'lsa)    |

![Tuist manifest xaritasi: qaysi fayl nimani ta'riflaydi va qayerda joylashadi](/lessons/images/tuist/tuist_02_manifest_map.svg)

Diagramma qaysi fayl nimaga javobgarligini ko'rsatadi: `Tuist.swift` repozitoriy ildizida turadi va "bu yer loyiha boshi" deb belgilaydi; har bir `Project.swift` o'z papkasida bitta `.xcodeproj` hosil qiladi; `Workspace.swift` project'larni bitta `.xcworkspace`'ga bog'laydi; `Tuist/Package.swift` esa tashqi paketlarni olib keladi. Tuist bularni o'qib, generate vaqtida tayyor Xcode fayllarini yozadi.

> **🧠 Esda tuting:** `import ProjectDescription` — manifestning birinchi qatori. Agar uni unutib qo'ysangiz, `tuist generate` "Cannot find type 'Project' in scope" degan kompilyatsiya xatosini beradi, chunki manifest oddiy Swift fayl sifatida kompilyatsiya qilinadi.

## Keling, tartib bilan — avval bitta project'dan (`Project.swift`), keyin target, settings, scheme, so'ng global konfiguratsiya (`Tuist.swift`), workspace va paketlar bilan tanishamiz. Dars oxirida (12-bo'lim) bularning hammasini bitta ishlaydigan loyihada birlashtiramiz.

## 3. Project.swift — loyihaning yuragi {#project-swift}

`Project.swift` bitta Xcode project'ni ta'riflaydi. U qaysi papkada tursa, `.xcodeproj` o'sha papkada, `name` dagi nom bilan hosil bo'ladi.

Eng muhim qoida Tuist hujjatida katta harf bilan yozilgan: **manifest ildizida faqat bitta `let project = Project(...)` o'zgaruvchisi bo'lishi kerak.** Kodni qayta ishlatmoqchi bo'lsangiz — Swift funksiyalariga chiqaring, lekin ildizda boshqa top-level `let`/`var` qoldirmang.

`Project` ning to'liq konstruktori (ProjectDescription manbasidagi `Project.swift` dan, Tuist 4.211.1):

```swift
// ProjectDescription — Project init (2026)
public init(
    name: String,
    organizationName: String? = nil,
    classPrefix: String? = nil,
    options: Options = .options(),
    packages: [Package.Dependency] = [],
    settings: Settings? = nil,
    targets: [Target] = [],
    schemes: [Scheme] = [],
    fileHeaderTemplate: FileHeaderTemplate? = nil,
    additionalFiles: [FileElement] = [],
    resourceSynthesizers: [ResourceSynthesizer] = .default
)
```

Har bir maydon nimaga kerak:

| Maydon                 | Vazifasi                                                                                                                |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `name`                 | Project nomi va `.xcodeproj` fayl nomi (`Kutubxona` → `Kutubxona.xcodeproj`).                                           |
| `organizationName`     | Yangi fayl yaratilganda Xcode header comment'idagi tashkilot nomi.                                                      |
| `classPrefix`          | Xcode yangi class yaratganda qo'yadigan prefiks (masalan `KX`).                                                         |
| `options`              | Project darajasidagi sozlamalar (`Project.Options`) — avtomatik scheme generatsiyasi, hududlar, bundle accessor va h.k. |
| `packages`             | Project'ga ulanadigan Swift paketlari (SPM-mos `Package.Dependency` modeli).                                            |
| `settings`             | Project darajasidagi build settings va configuration'lar.                                                               |
| `targets`              | Target'lar ro'yxati — loyihaning asosiy qismi.                                                                          |
| `schemes`              | Custom scheme'lar (bermasangiz, Tuist avtomatik yaratadi).                                                              |
| `fileHeaderTemplate`   | Xcode fayl shablonlari uchun maxsus header.                                                                             |
| `additionalFiles`      | Build qilinmaydigan, lekin Xcode'da ko'rinadigan fayllar (masalan, `Dangerfile.swift`, hujjatlar).                      |
| `resourceSynthesizers` | Resource'lar uchun type-safe accessor'lar generatsiyasi (keyingi darslarda).                                            |

> **⚠️ Diqqat:** `organizationName`, `classPrefix`, `settings` — hammasi optional (`nil` yoki default). Eng minimal valid project atigi `name` va `targets` bilan ishlaydi. Qolgan maydonlarni kerak bo'lgandagina qo'shing (YAGNI) — manifestni behuda shishirmang.

`Project.Options` ham bir qancha foydali sozlamani ochadi (manbadan, `ProjectOptions.swift`):

```swift
// ProjectDescription — Project.Options.options(...)
options: .options(
    automaticSchemesOptions: .enabled(),   // avtomatik scheme'lar qanday guruhlanadi
    defaultKnownRegions: ["en", "uz"],     // localization hududlari
    developmentRegion: "uz",               // asosiy til
    disableBundleAccessors: false,
    disableSynthesizedResourceAccessors: false,
    xcodeProjectName: nil                  // .xcodeproj uchun boshqa nom
)
```

Masalan, `automaticSchemesOptions: .disabled` qilsangiz, Tuist avtomatik scheme yaratmaydi — hamma scheme'ni o'zingiz `schemes:` da yozasiz (buni 7-bo'limda ko'ramiz).

---

## 4. Target anatomiyasi — bitta build mahsuloti {#target-anatomiyasi}

**Target** — loyihaning build qilinadigan bitta bo'lagi: bitta `.app`, bitta `.framework`, bitta test bundle. Tuist'da target `.target(...)` factory funksiyasi bilan yaratiladi. 2026-yilda bu factory **26 ta parametrga** ega (manbadagi `Target.swift`). Hammasini yodlash shart emas — ko'pini default qiymatda qoldirasiz. Eng muhim boshlang'ich parametrlar tartibi:

```swift
// ProjectDescription — Target.target(...) (muhim parametrlar)
public static func target(
    name: String,
    destinations: Destinations,
    product: Product,
    productName: String? = nil,
    bundleId: String,
    deploymentTargets: DeploymentTargets? = nil,
    infoPlist: InfoPlist? = .default,
    sources: SourceFilesList? = nil,
    resources: ResourceFileElements? = nil,
    buildableFolders: [BuildableFolder] = [],
    copyFiles: [CopyFilesAction]? = nil,
    headers: Headers? = nil,
    entitlements: Entitlements? = nil,
    scripts: [TargetScript] = [],
    additionalHashingInputs: [HashingInput] = [],
    dependencies: [TargetDependency] = [],
    settings: Settings? = nil,
    // ... coreDataModels, environmentVariables, launchArguments,
    //     additionalFiles, buildRules, mergedBinaryType, mergeable,
    //     onDemandResourcesTags, metadata
) -> Target
```

![Target anatomiyasi: identifikatsiya, platforma, mahsulot, fayllar, bog'liqliklar va settings guruhlari](/lessons/images/tuist/tuist_02_target_anatomy.svg)

Diagramma target maydonlarini olti guruhga ajratadi: kim ekanligi (`name`, `bundleId`), qayerda ishlashi (`destinations`, `deploymentTargets`), nima ishlab chiqarishi (`product`), nimadan qurilishi (`sources`/`resources`/`buildableFolders`), nimaga tayanishi (`dependencies`) va qanday sozlanishi (`settings`, `infoPlist`, `entitlements`, `scripts`).

### 4.1 `destinations` vs `deploymentTargets` — ikki xil narsa

Bu ikkisi boshlang'ichlar uchun eng ko'p chalkashlik beradigan juftlik:

- **`destinations`** — target **qaysi qurilma oilalarida** ishlashi (`.iPhone`, `.iPad`, `.mac`, `.appleVision`...). Bu `Set<Destination>`.
- **`deploymentTargets`** — har bir platformaning **minimal OS versiyasi** (`iOS 17.0`, `macOS 14.0`...).

`destinations: .iOS` — bu aslida bitta qulay to'plam. Manbadagi `Destination.swift` ga ko'ra:

```swift
// ProjectDescription — Destinations.iOS
public static let iOS: Destinations = [.iPhone, .iPad, .macWithiPadDesign]
```

Buni men `tuist dump` chiqishida ham ko'rdim — `.iOS` aynan shu uchtaga yoyilgan:

```text
"destinations": [ "iPhone", "iPad", "macWithiPadDesign" ]
```

Ya'ni `destinations: .iOS` yozsangiz, ilovangiz iPhone, iPad va "Mac (iPad dizayni bilan, ya'ni Apple Silicon Mac'da iPad ilovasi sifatida)" ni qo'llab-quvvatlaydi. Faqat iPhone kerak bo'lsa: `destinations: [.iPhone]`.

### 4.2 `product` — nima ishlab chiqariladi

`Product` enum'i (manbadagi `Product.swift`, raw qiymatlari bilan) — eng ko'p ishlatiladiganlari:

| Qiymat                               | Tushuntirish                                                                          |
| ------------------------------------ | ------------------------------------------------------------------------------------- |
| `.app`                               | iOS/macOS ilova — foydalanuvchi o'rnatadigan asosiy mahsulot.                         |
| `.framework`                         | Dynamic framework — run vaqtida bog'lanadi.                                           |
| `.staticFramework`                   | Static framework — compile vaqtida ilovaga qo'shiladi (modul uchun ko'p ishlatiladi). |
| `.staticLibrary` / `.dynamicLibrary` | Static / dynamic kutubxona.                                                           |
| `.unitTests`                         | Unit test bundle.                                                                     |
| `.uiTests`                           | UI test bundle.                                                                       |
| `.bundle`                            | Resource bundle (iOS'da hozircha resource bundle'lar uchun).                          |
| `.appExtension`                      | App extension (Widget, Share, Notification...).                                       |
| `.appClip`                           | App Clip (iOS).                                                                       |
| `.macro`                             | Swift Macro target.                                                                   |

> **💡 Pro tip:** Modullar uchun default tanlov — `.staticFramework`. Bu link vaqtini kamaytiradi va dynamic framework'ning run vaqtidagi yuklanish xarajatini yo'q qiladi. Dynamic (`.framework`) ni faqat kerak bo'lgandagina — masalan, bir framework'ni bir nechta jarayon (app + extension) baham ko'rganda tanlang.

### 4.3 `bundleId` va `infoPlist`

`bundleId` — majburiy (`Target.swift` da default yo'q). Teskari-domen uslubida yozing: `uz.azimjondev.kutubxona`.

`infoPlist` ning to'rt varianti bor (manbadagi `InfoPlist.swift`):

```swift
// 1) Default — Tuist Info.plist ni to'liq o'zi generate qiladi
infoPlist: .default

// 2) Default ustiga qo'shimcha kalitlar
infoPlist: .extendingDefault(with: [
    "UILaunchScreen": [:],
    "CFBundleDisplayName": "Kutubxona",
])

// 3) Mavjud fayl
infoPlist: .file(path: "Support/Info.plist")

// 4) xcconfig o'zgaruvchisi (har configuration uchun boshqa plist)
infoPlist: .variable("$(INFO_PLIST_FILE)")
```

Default `.default` — bu aslida `.extendingDefault(with: [:])`. Ko'p holatlarda `.extendingDefault(with: [...])` eng qulay: Tuist majburiy kalitlarni o'zi qo'yadi, siz faqat o'zingiznikini qo'shasiz.

### 4.4 `dependencies` — target nimaga tayanadi

Bog'liqliklar `TargetDependency` ro'yxati. Eng ko'p ishlatiladiganlari:

```swift
dependencies: [
    .target(name: "BooksKit"),                              // shu project ichidagi target
    .project(target: "Katalog", path: "../Features/Katalog"), // boshqa project'dagi target
    .external(name: "Alamofire"),                           // Tuist/Package.swift dagi tashqi paket
    .sdk(name: "StoreKit", type: .framework),               // Apple SDK framework
]
```

Bu darsda biz `.target` va `.project` ni ishlatamiz; tashqi paketlar (`.external`) va ularning turlari — keyingi (T04) darsning mavzusi: [04. Dependency'lar](/ios-lessons/tuist-04-dependencies).

> **🧠 Esda tuting:** `tuist dump` chiqishida bog'liqlik aniq ko'rinadi — men yuqoridagi loyihada `BooksApp` ni `tuist dump` qilganimda, uning bog'liqligi shunday chiqdi:
>
> ```text
> "dependencies": [ { "target": { "name": "BooksKit", "status": "required" } } ]
> ```

---

## 5. buildableFolders vs sources/resources globs (2026 yangiligi) {#buildable-folders}

Target'ning kodi va resource'larini Tuist'ga ikki xil usulda ko'rsatish mumkin. Bu — 2026-yilda bilib qo'yish kerak bo'lgan eng muhim farqlardan biri.

### 5.1 Eski (lekin hali ishlaydigan) usul — globs

An'anaviy usul `sources` va `resources` ga **glob pattern** berish:

```swift
.target(
    name: "BooksApp",
    // ...
    sources: ["BooksApp/Sources/**"],
    resources: ["BooksApp/Resources/**"]
)
```

`**` — rekursiv (barcha ichki papkalar). Ba'zi fayllarni chiqarib tashlash ham mumkin (manbadagi `SourceFilesList.swift`):

```swift
sources: [
    .glob("BooksApp/Sources/**", excluding: "BooksApp/Sources/**/*Mock.swift"),
]
```

Bu usulning kamchiligi: har safar yangi `.swift` fayl qo'shganingizda yoki o'chirganingizda, glob natijasi o'zgaradi — demak `.xcodeproj` o'zgaradi, demak `tuist generate` ni qayta yurgizish kerak. Katta jamoada bu tez-tez takrorlanadi.

### 5.2 Yangi usul — buildableFolders (Xcode 16 synchronized groups)

Xcode 16 "synchronized root group" (sinxronlanadigan papka) tushunchasini kiritdi: Xcode papkaning ichini **o'zi** kuzatadi, fayllar ro'yxatini `.pbxproj` da saqlamaydi. Bu aynan `.pbxproj` merge conflict'larini kamaytirish uchun qilingan.

Tuist buni `buildableFolders` orqali qo'llab-quvvatlaydi (Tuist changelog, 2025-08-11, #7984). Manbadagi `BuildableFolder.swift` izohi buni shunday ta'riflaydi: "Xcode 16 da branch'lar orasida fayl referenslari tez-tez o'zgarishidan kelib chiqadigan Git conflict'larini kamaytirish uchun kiritilgan."

```swift
.target(
    name: "BooksApp",
    // ...
    buildableFolders: [
        "BooksApp/Sources",
        "BooksApp/Resources",
    ]
)
```

Bu yerda siz **papkani** ko'rsatasiz, glob emas. Papka ichidagi hamma fayl avtomatik target'ga kiradi. Natijada: yangi fayl qo'shsangiz, `tuist generate` ni qayta yurgizish shart emas — Xcode papkani o'zi o'qiydi.

> **🚀 Motivatsiya:** `buildableFolders` bilan "fayl qo'shdim → yana generate qilishim kerakmi?" degan savol yo'qoladi. Ayniqsa AI agent, CLI yoki CI yangi fayl generatsiya qilganda, hech narsa qayta generate qilinmasligi juda qulay. Shuning uchun 2026-yilda yangi modullar uchun `buildableFolders` tavsiya etiladi.

`tuist dump` natijasi `buildableFolders` ni qanday ko'rishini tasdiqlaydi (men o'z loyihamda tekshirdim):

```text
"buildableFolders": [
  { "path": { "pathString": "BooksApp/Sources", "type": "relativeToManifest" },
    "optional": false, "exceptions": { "exceptions": [] } },
  { "path": { "pathString": "BooksApp/Resources", "type": "relativeToManifest" },
    "optional": false, "exceptions": { "exceptions": [] } }
]
```

### 5.3 Istisnolar (exceptions)

Agar papkaning aksariyatini olib, ba'zi fayllarni chiqarmoqchi yoki ularga maxsus compiler flag bermoqchi bo'lsangiz, `.folder(_:exceptions:)` dan foydalaning. Tuist'ning rasmiy fixture'laridagi real misol:

```swift
buildableFolders: [
    .folder("App/Sources", exceptions: .exceptions([
        .exception(
            excluded: ["App/Sources/Excluded.swift"],
            compilerFlags: ["App/Sources/WithCompilerFlags.swift": "-print-stats"]
        ),
    ])),
]
```

> **⚠️ Diqqat:** `buildableFolders` — bu Xcode 16+ imkoniyati; Xcode 15 va undan eski versiyalar uni qo'llab-quvvatlamaydi. Agar jamoangiz hali eski Xcode'da bo'lsa, `sources`/`resources` globs'da qoling. Xcode 27.1 da `buildableFolders` men tekshirgan loyihada muammosiz ishladi (generate → build → test — hammasi yashil).

### 5.4 Qaysi birini tanlash?

| Usul                        | Afzalligi                                                             | Kamchiligi                                                   |
| --------------------------- | --------------------------------------------------------------------- | ------------------------------------------------------------ |
| `buildableFolders`          | Fayl qo'shganda qayta generate shart emas; kamroq `.pbxproj` conflict | Xcode 16+ talab qiladi; per-fayl nazorat `exceptions` orqali |
| `sources`/`resources` globs | Har qanday Xcode versiyasida ishlaydi; glob bilan nozik nazorat       | Fayl qo'shilsa/o'chsa qayta generate kerak                   |

---

## 6. Settings, configuration va xcconfig {#settings}

Build settings — bu `SWIFT_VERSION`, `IPHONEOS_DEPLOYMENT_TARGET`, `SWIFT_OPTIMIZATION_LEVEL` kabi Xcode sozlamalari. Tuist'da ular `Settings` turi orqali beriladi (manbadagi `Settings.swift`).

Eng oddiy shakl — `base` (hamma configuration'ga umumiy) + Debug/Release:

```swift
settings: .settings(
    base: [
        "SWIFT_VERSION": "6.0",
        "IPHONEOS_DEPLOYMENT_TARGET": "17.0",
    ],
    debug: ["SWIFT_OPTIMIZATION_LEVEL": "-Onone"],
    release: ["SWIFT_OPTIMIZATION_LEVEL": "-O"]
)
```

Lekin production loyihalarda build settings'ni Swift ichida emas, **`.xcconfig` fayllarda** saqlash tavsiya etiladi. Shunda sozlamalar matn fayl bo'lib, diff/review qulay bo'ladi va Swift manifestni shishirmaydi. Buning uchun custom configuration'lar bilan `.settings(base:configurations:)` shaklini ishlating:

```swift
settings: .settings(
    base: [
        "SWIFT_VERSION": "6.0",
        "IPHONEOS_DEPLOYMENT_TARGET": "17.0",
    ],
    configurations: [
        .debug(name: "Debug", xcconfig: "Config/Debug.xcconfig"),
        .release(name: "Release", xcconfig: "Config/Release.xcconfig"),
    ],
    defaultConfiguration: "Debug"
)
```

`.debug(name:settings:xcconfig:)` va `.release(name:settings:xcconfig:)` — ikkalasi ham `name`, ixtiyoriy `settings` (dictionary) va `xcconfig` (fayl yo'li) oladi. `defaultConfiguration` — command-line'dan build qilganda qaysi configuration ishlatilishini belgilaydi (bermasangiz, alifbo bo'yicha birinchi debug configuration tanlanadi).

Mana men tekshirgan `Config/Debug.xcconfig`:

```text
// Config/Debug.xcconfig
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG
SWIFT_OPTIMIZATION_LEVEL = -Onone
ENABLE_TESTABILITY = YES
```

`tuist dump` chiqishi xcconfig aynan configuration'ga bog'langanini tasdiqladi:

```text
"configurations": [
  { "name": { "rawValue": "Debug" }, "variant": "debug",
    "xcconfig": { "pathString": "Config/Debug.xcconfig", "type": "relativeToManifest" } },
  { "name": { "rawValue": "Release" }, "variant": "release",
    "xcconfig": { "pathString": "Config/Release.xcconfig", "type": "relativeToManifest" } }
]
```

### 6.1 Project-level vs target-level settings

Settings'ni ham project darajasida (hamma target'ga), ham bitta target ichida berish mumkin. Target-level settings project-level'ni **ustidan yozadi** (override):

```swift
.target(
    name: "BooksApp",
    // ...
    settings: .settings(base: [
        "DEVELOPMENT_TEAM": "ABCDE12345",
    ])
)
```

### 6.2 DefaultSettings — Tuist qancha tayyor sozlama qo'shadi

`.settings(...)` da `defaultSettings:` parametri Tuist target'ga qancha tayyor sozlama qo'shishini boshqaradi (manbadagi `Settings.swift`):

- `.recommended` (default) — Tuist tavsiya qiladigan to'liq to'plam, jumladan ogohlantirish flag'lari.
- `.essential` — faqat minimal kerakli sozlamalar (`PRODUCT_NAME`, `TARGETED_DEVICE_FAMILY` va h.k.).
- `.none` — Tuist hech qanday build setting qo'shmaydi (hammasini o'zingiz berasiz).

```swift
settings: .settings(
    base: ["SWIFT_VERSION": "6.0"],
    defaultSettings: .essential   // Tuist kamroq aralashsin
)
```

> **💡 Pro tip:** Ba'zi sozlamani Tuist'ning tavsiya qiladigan qiymatidan tozalamoqchi bo'lsangiz, hammasini `.none` qilib tashlamang — `.recommended(excluding: ["CLANG_ENABLE_OBJC_WEAK"])` ko'rinishida faqat kerakli kalitni chiqarib tashlang.

---

## 7. Scheme'lar — avtomatik va custom {#schemes}

**Scheme** — "qanday build / test / run / profile / archive qilish kerak"ni belgilaydigan konfiguratsiya. Tuist'ning eng qulay xususiyatlaridan biri: siz scheme yozmasangiz ham, u **har bir target uchun avtomatik scheme** yaratadi.

Men yuqoridagi `Kutubxona` loyihasini generate qilib, `xcodebuild -list` bilan scheme'larni ko'rganimda, natija shunday bo'ldi:

```text
Information about workspace "Kutubxona":
    Schemes:
        BooksApp
        BooksKit
        Generate Project
        Kutubxona
        Kutubxona-Workspace
```

Bu ro'yxatni o'qib chiqaylik:

- `BooksApp`, `BooksKit` — har bir non-test target uchun avtomatik scheme.
- `Kutubxona` — men qo'lda yozgan custom scheme (pastda).
- `Kutubxona-Workspace` — butun workspace'ni qamrab oluvchi scheme (barcha target'lar birga).
- `Generate Project` — `tuist generate` ni qayta yurgizadigan maxsus scheme. U default bo'yicha qo'shiladi (buni `Tuist.swift` dagi `includeGenerateScheme` boshqaradi, 8-bo'limga qarang).

> **🧠 Esda tuting:** Test target alohida scheme bo'lmaydi — u o'zi tegishli target scheme'iga biriktiriladi. Men `BooksKitTests` ni yaratdim, lekin ro'yxatda `BooksKitTests` degan scheme yo'q: uning testlari `BooksKit` (va men yozgan `Kutubxona`) scheme'ining test action'ida ishlaydi. Shu sabab `tuist test BooksKitTests` xato beradi — scheme nomini (`BooksKit` yoki `Kutubxona`) berish kerak.

### Custom scheme yozish

Avtomatik scheme kamlik qilsa (masalan, test action'ni aniq belgilamoqchi bo'lsangiz yoki maxsus argument qo'shmoqchi bo'lsangiz), `schemes:` da o'zingiz yozasiz. `Scheme.scheme(...)` ning signaturasi (manbadagi `Scheme.swift`):

```swift
// ProjectDescription — Scheme.scheme(...)
.scheme(
    name: "Kutubxona",
    shared: true,                                      // repozitoriyga commit qilinadimi
    hidden: false,                                     // Xcode dropdown'da ko'rinadimi
    buildAction: .buildAction(targets: ["BooksApp"]),
    testAction: .targets(["BooksKitTests"]),
    runAction: .runAction(configuration: .debug)
)
```

- `buildAction` — qaysi target'lar build qilinadi (`.buildAction(targets:)`). `findImplicitDependencies: false` berib, Xcode'ning implicit dependency izlashini o'chirib qo'yish ham mumkin.
- `testAction` — qaysi testlar ishlaydi (`.targets([...])`) yoki test plan'lar (`.testPlans([...])`).
- `runAction` — qaysi configuration bilan ishga tushiriladi.
- `archiveAction`, `profileAction`, `analyzeAction` — kerak bo'lsa qo'shiladi.

> **💡 Pro tip:** `shared: true` — scheme `.xcodeproj`'ning `xcshareddata` papkasiga tushadi, ya'ni `xcodebuild` uni command-line'dan ko'radi. CI'da ishlatiladigan har qanday scheme `shared: true` bo'lishi shart, aks holda CI "scheme topilmadi" deydi.

> **⚠️ Diqqat (API o'zgarishi):** `runAction` da ilgari `executable: "AppName"` (string → `TargetReference`) ishlatilardi. 4.211.1 da bu shakl `deprecated`; yangi shakl `executable: .executable("AppName")` yoki umuman bermaslik (`.runAction(configuration: .debug)`). Oddiy, bitta app target'li scheme'da `executable`'ni tushirib qoldirsangiz, Tuist uni o'zi aniqlaydi.

---

## 8. Tuist.swift — 2026 global konfiguratsiya {#tuist-swift}

`Tuist.swift` ikki vazifani bajaradi: (1) repozitoriyda **loyiha ildizini** belgilaydi (Tuist yuqoriga qarab shu faylni qidiradi), (2) global konfiguratsiyani saqlaydi. U eski Tuist 3 dagi `Config.swift` ning o'rnini bosgan — `Config.swift` endi yo'q.

### 8.1 Minimal Tuist.swift (2026 shakli)

```swift
// Tuist.swift
import ProjectDescription

let tuist = Tuist(project: .tuist())
```

Diqqat qiling: `Tuist(project: .tuist())`. Bu — **2026-yilgi to'g'ri shakl**. Men buni Tuist'ning rasmiy `generated_app_with_buildable_folders` fixture'ida ham, o'z loyihamda ham aynan shunday ko'rdim.

> **⚠️ Diqqat (eng muhim 2026 o'zgarishi):** Internetdagi ko'p eski qo'llanma `Tuist(compatibleXcodeVersions:..., generationOptions: ...)` ko'rinishini ko'rsatadi. Manba kodida (`Tuist.swift`) bu eski konstruktor endi `@available(*, deprecated)` bilan belgilangan va izohda: _"Use the new .init(project: .tuist(...)) that nests the property-related attributes into project."_ Ya'ni `compatibleXcodeVersions`, `plugins`, `generationOptions`, `installOptions` endi `.tuist(...)` ning **ichiga** ko'chgan.

### 8.2 Kengaytirilgan Tuist.swift

Konfiguratsiya qo'shmoqchi bo'lsangiz, hammasi `.tuist(...)` ichiga tushadi (manbadagi `TuistProject.swift`):

```swift
// Tuist.swift
import ProjectDescription

let tuist = Tuist(
    project: .tuist(
        compatibleXcodeVersions: "27.1",          // faqat shu Xcode versiyasi
        generationOptions: .options(
            defaultSwiftVersion: "6.0",           // SWIFT_VERSION bermagan target'lar uchun
            onOutdatedDependencies: .warn,        // Package.resolved eskirsa nima qilish
            projectFormat: .pbxproj               // yoki .xcproj (Xcode 27.2 JSON5 formati)
        )
    )
)
```

`compatibleXcodeVersions` enum'i (manbadagi `CompatibleXcodeVersions.swift`): `.all` (default), `.exact("27.1")`, `.upToNextMajor("27.0")`, `.upToNextMinor("27.1")`. String literal (`"27.1"`) avtomatik `.exact` ga aylanadi.

`GenerationOptions` 2026-yilda ancha boyigan (manbadagi `ConfigGenerationOptions.swift`). Eng foydali maydonlari:

| Maydon                   | Vazifasi                                                                                                 |
| ------------------------ | -------------------------------------------------------------------------------------------------------- |
| `defaultSwiftVersion`    | `SWIFT_VERSION` bermagan lokal target'larga qo'yiladigan Swift tili rejimi (default `"5"`).              |
| `onOutdatedDependencies` | `Package.resolved` o'zgargan bo'lsa: `.warn` (default), `.install` (avtomatik `tuist install`), `.fail`. |
| `projectFormat`          | `.pbxproj` (default) yoki `.xcproj` — Xcode 27.2 ning eksperimental JSON5 project formati.               |
| `includeGenerateScheme`  | "Generate Project" scheme'ini qo'shish (default `true` — shuning uchun yuqorida ko'rgan edik).           |
| `enableCaching`          | Xcode compilation cache sozlamalarini qo'shish.                                                          |
| `warningsAsErrors`       | Generation ogohlantirishlarini xatoga aylantirish.                                                       |

### 8.3 `enforceExplicitDependencies` — endi deprecated

Eski qo'llanmalarda `generationOptions: .options(enforceExplicitDependencies: true)` degan sozlama bor edi — u implicit dependency'larni build vaqtida taqiqlardi. **4.211.1 da bu deprecated.** Manba kodidagi izoh aniq aytadi:

```text
enforceExplicitDependencies is deprecated.
Use 'tuist inspect dependencies --only implicit' instead.
```

Ya'ni endi implicit dependency'ni **build vaqtida bloklash** o'rniga, uni **alohida buyruq bilan tekshirish** tavsiya etiladi. Bu buyruqni amalda 11-bo'limda ishlatamiz va u qanday xato berishini ko'ramiz.

### 8.4 `fullHandle` — server bilan bog'lanish

Agar Tuist serverining cache/insights imkoniyatlaridan foydalanmoqchi bo'lsangiz, loyiha "handle"ini shu yerga yozasiz:

```swift
let tuist = Tuist(
    fullHandle: "mening-tashkilotim/kutubxona",
    project: .tuist()
)
```

> **⚠️ Diqqat:** `fullHandle` faqat Tuist serveriga (tuist.dev yoki self-hosted) ulanganda kerak va hisob/obuna talab qiladi. Lokal generate, build, test, hatto binary cache uchun u shart emas — biz bu darsda hech qanday hisobsiz ishladik. Server imkoniyatlari T07 va T15 darslarida.

---

## 9. Workspace.swift — ko'p loyihali workspace {#workspace-swift}

Default holatda `tuist generate` bitta `.xcworkspace` yaratadi: u generate qilinayotgan project'ni va uning bog'liqliklari project'larini o'z ichiga oladi. Ya'ni oddiy holatda `Workspace.swift` **shart emas**.

`Workspace.swift` faqat o'zingiz workspace tarkibini boshqarmoqchi bo'lsangiz kerak bo'ladi — masalan, bir-biriga bog'liq bo'lmagan bir nechta project'ni bitta oynada ko'rmoqchisiz yoki workspace'ga hujjat fayllarini qo'shmoqchisiz.

`Workspace` ning konstruktori (manbadagi `Workspace.swift`):

```swift
// ProjectDescription — Workspace init
public init(
    name: String,
    projects: [Path],
    schemes: [Scheme] = [],
    fileHeaderTemplate: FileHeaderTemplate? = nil,
    additionalFiles: [FileElement] = [],
    generationOptions: GenerationOptions = .options()
)
```

Men ikki loyihali workspace tuzib, uni tekshirdim. Struktura:

```text
MultiLoyiha/
├── Tuist.swift
├── Workspace.swift
├── Tuist/
│   └── Package.swift
├── Apps/
│   └── Kutubxona/
│       ├── Project.swift        (.app, Katalog'ga bog'liq)
│       └── Sources/App.swift
└── Features/
    └── Katalog/
        ├── Project.swift        (.framework)
        └── Sources/Katalog.swift
```

`Workspace.swift`:

```swift
// Workspace.swift
import ProjectDescription

let workspace = Workspace(
    name: "MultiLoyiha",
    projects: [
        "Apps/Kutubxona",
        "Features/Katalog",
    ]
)
```

App project'ning Katalog'ga bog'lanishi — `.project(target:path:)` orqali:

```swift
// Apps/Kutubxona/Project.swift (qisqartirilgan)
.target(
    name: "Kutubxona",
    destinations: .iOS,
    product: .app,
    bundleId: "uz.azimjondev.kutubxona",
    deploymentTargets: .iOS("17.0"),
    infoPlist: .extendingDefault(with: ["UILaunchScreen": [:]]),
    buildableFolders: ["Sources"],
    dependencies: [
        .project(target: "Katalog", path: "../../Features/Katalog"),
    ]
)
```

`tuist generate --no-open` natijasi (real chiqish):

```text
Generating workspace MultiLoyiha.xcworkspace
Generating project Katalog
Generating project Kutubxona
Total time taken: 0.782s
✔ Success
  Project generated.
```

Ikkala project ham generate bo'ldi va `xcodebuild -list` workspace scheme'larini ko'rsatdi: `Katalog`, `Kutubxona`, `MultiLoyiha-Workspace`, `Generate Project`.

> **🧠 Esda tuting:** `Workspace.swift` da faqat asosiy project'lar yo'lini berasiz — ularning bog'liqliklari (dependency graph) **avtomatik** qo'shiladi. Men `Workspace.swift` da faqat ikkita yo'l berdim, lekin Katalog Kutubxona'ning bog'liqligi bo'lgani uchun u ham to'g'ri ulandi. Build system bog'liqliklarni to'g'ri hal qilishi uchun bu kerak.

### Mono-project yoki multi-project?

Ko'p so'raladigan savol: bitta katta `Project.swift` yaxshiroqmi yoki har modul uchun alohida project? Tuist hujjati buni ochiq qoldiradi, lekin o'z sababini aytadi:

> **🏢 Real-world:** Tuist jamoasi o'z loyihasida **mono-project** (bitta `Project.swift`) ishlatadi, chunki: (1) cold generate tezroq — kamroq manifest fayl kompilyatsiya qilinadi; (2) kodni qayta ishlatish uchun `.xcodeproj` o'rniga `ProjectDescriptionHelpers` dan foydalaniladi. `.xcodeproj` Git'ga tushmagani uchun, "mono-project Git conflict beradi" degan klassik sabab Tuist'da yo'qoladi — demak tanlov sizniki (manba: Tuist "Manifests" hujjati). Multi-project'ni ko'proq domenlarni ajratish uchun, Xcode'ning tavsiya qilgan strukturasiga yaqinroq bo'lish uchun tanlashadi.

Modullarni alohida project qilishning chuqur arxitekturasi (TMA, µFeature) — T06 darsida: [06. Modular arxitektura](/ios-lessons/tuist-06-modular-architecture).

---

## 10. Tuist/Package.swift va PackageSettings {#package-swift}

Tashqi SPM paketlari (Alamofire, Kingfisher...) Tuist'da `Project.swift` da emas, alohida **`Tuist/Package.swift`** faylida e'lon qilinadi. Bu — Tuist 4 ning modeli (eski Tuist 3 dagi `Tuist/Dependencies.swift` va `tuist fetch` endi yo'q).

Fayl oddiy SPM `Package.swift` ga o'xshaydi, lekin `#if TUIST` bloki bilan Tuist-ga xos sozlamalarni qo'shadi (manbadagi `PackageSettings.swift`):

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

#if TUIST
    import ProjectDescription

    let packageSettings = PackageSettings(
        productTypes: [
            "Alamofire": .framework,   // default .staticFramework, buni .framework qildik
        ],
        baseProductType: .staticFramework
    )
#endif

let package = Package(
    name: "Kutubxona",
    dependencies: [
        .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
    ]
)
```

Bu yerda nima bo'layapti:

- `let package = Package(...)` — oddiy SPM e'loni: qaysi paketlar, qaysi versiya.
- `#if TUIST ... #endif` — faqat Tuist manifestni o'qiyotganda kompilyatsiya bo'ladi; oddiy `swift build` buni ko'rmaydi.
- `PackageSettings` — paketlar qanday product turiga aylanishini boshqaradi. **Paketlar default bo'yicha `.staticFramework`** ga aylanadi (`baseProductType`), lekin `productTypes` orqali ayrimini `.framework` qilish mumkin.

Paketni ulagandan keyin `tuist install` ni yurgizasiz — u paketlarni yechib, `Tuist/.build` papkasiga yuklaydi.

> **⚠️ Diqqat:** Tashqi paketlar `Tuist/.build` (SwiftPM'ning build papkasi) ichiga yechiladi — **`Tuist/Dependencies` emas** (bu Tuist 3 ning eski yo'li edi). `Tuist/.build` ni `.gitignore` ga qo'shing. Target'da paketdan foydalanish esa `.external(name: "Alamofire")` orqali bo'ladi.

> **🧠 Esda tuting:** Bu darsda biz tashqi paketlarni chuqur ishlatmaymiz — bizning asosiy misolimiz (`Kutubxona`) hech qanday tashqi paketga tayanmaydi, shuning uchun uning `Tuist/Package.swift` dagi `dependencies` ro'yxati bo'sh. Paketlar, XCFramework, binary cache va `.external` ning hamma turlari — keyingi dars (T04) mavzusi: [04. Dependency'lar](/ios-lessons/tuist-04-dependencies).

---

## 11. tuist edit, tuist dump va manifest caching {#edit-dump-cache}

Manifestlarni yozish, ko'rish va ularning ichki mexanizmini tushunishda uchta narsa yordam beradi: `tuist edit`, `tuist dump` va manifest caching.

![Generate oqimi: manifest Swift fayllari → ProjectDescription kompilyatsiyasi (cache) → JSON model → .xcodeproj/.xcworkspace](/lessons/images/tuist/tuist_02_generation_flow.svg)

Diagramma generate mexanizmini ko'rsatadi: Tuist avval manifest `.swift` fayllarini `ProjectDescription` bilan **kompilyatsiya** qiladi (natija cache'lanadi), keyin ularni ishga tushirib JSON **model** chiqaradi (`tuist dump` shuni ko'rsatadi), oxirida shu modeldan `.xcodeproj`/`.xcworkspace` ni **generate** qiladi.

### 11.1 tuist edit — autocomplete bilan tahrirlash

Manifestni istalgan matn muharririda yozsa bo'ladi, lekin Tuist maxsus workflow beradi:

```bash
tuist edit
```

Bu buyruq nima qiladi (rasmiy "Editing" hujjatidan): global papkada **vaqtinchalik Xcode project** yaratadi va uni Xcode'da ochadi. Ichida `Manifests` degan papka bo'ladi — uni build qilib, hamma manifestingiz to'g'riligini tekshirishingiz mumkin. Xcode'da bo'lgani uchun **autocomplete, syntax highlighting va error checking** ishlaydi.

`tuist edit --help` ko'rsatgan asosiy flaglar (men o'zim yurgizdim):

```text
USAGE: tuist edit [--path <path>] [--permanent] [--only-current-directory]

  -P, --permanent/--no-permanent   Loyihani joriy papkada yaratadi va jarayonni
                                    bloklamaydi (default: --no-permanent)
  -o, --only-current-directory      Faqat joriy papkadagi manifestni qo'shadi
```

Default `tuist edit` jarayonni bloklab turadi (Xcode yopilguncha terminal band bo'ladi). Shuning uchun amaliy workflow: bitta terminal oynasida `tuist edit` ni ushlab turasiz (tahrirlash uchun), boshqasida `tuist generate` ni yurgizasiz.

> **🧠 Esda tuting:** Tahrirlashni **generate qilingan** `.xcodeproj` dan qilib bo'lmaydi — bu ataylab shunday qilingan. Shunday qilib generate qilingan project Tuist'ga bog'lanib qolmaydi, kelajakda Tuist'dan voz kechmoqchi bo'lsangiz oson chiqib ketasiz. `tuist edit` manifestlarni `**/{Manifest}.swift` glob'i bilan topadi (loyiha ildizidan, ya'ni `Tuist.swift` turgan joydan). Shuning uchun ildizda to'g'ri `Tuist.swift` bo'lishi shart.

### 11.2 tuist dump — manifest nimaga aylanganini ko'rish

`tuist dump` manifestingizni Tuist ichki **model**iga aylantirib, JSON ko'rinishida chiqaradi. Bu — manifestingiz haqiqatan nima bo'lishini ko'rishning eng aniq usuli (debug uchun oltin):

```bash
tuist dump project       # Project.swift ni JSON qilib chiqaradi
tuist dump workspace     # Workspace.swift ni
tuist dump config        # Tuist.swift ni
```

Men `Kutubxona` loyihamda `tuist dump project` yurgizganimda, 510 qatorlik JSON chiqdi. Shu yerda, masalan, `destinations: .iOS` ning aslida `["iPhone", "iPad", "macWithiPadDesign"]` ekanini, xcconfig'larning configuration'ga bog'langanini va bog'liqliklarni aniq ko'rdim (yuqoridagi bo'limlarda shu chiqishlardan iqtibos keltirdim).

> **💡 Pro tip:** Biror sozlama ishlamayotgandek tuyulsa, taxmin qilish o'rniga `tuist dump` qiling. Masalan, "`destinations` to'g'ri yoyilganmi?", "xcconfig bog'landimi?" degan savolga JSON darhol javob beradi.

### 11.3 Manifest caching — nega birinchi generate sekinroq?

Manifestlar Swift bo'lgani uchun kompilyatsiya qilinadi. Tuist bu kompilyatsiya natijasini cache'laydi (`~/.cache/tuist/Manifests`). Shuning uchun **birinchi** generate sekinroq, keyingilari tezroq.

Men buni Apple M4 Pro (12 core, 24 GB), Tuist 4.211.1, Xcode 27.1 da o'lchadim:

| Holat                                     | Vaqt                     | Izoh                                                                  |
| ----------------------------------------- | ------------------------ | --------------------------------------------------------------------- |
| Birinchi generate (manifest cache bo'sh)  | **1.408 s**              | Tuist "It might take a while if the cache is empty" deb ogohlantirdi  |
| Takroriy generate (manifest cache issiq)  | **~0.6 s** (0.56–0.64 s) | manifest qayta kompilyatsiya qilinmaydi                               |
| Manifestni o'zgartirgandan keyin generate | **~0.78 s**              | hash o'zgardi → faqat o'zgargan manifest qayta kompilyatsiya qilinadi |

Ya'ni bu kichik loyihada manifest kompilyatsiyasi ~0.8 soniya tejamkorlik beradi. Yuzlab manifest fayli bo'lgan katta loyihada bu farq ancha kattaroq — shuning uchun katta loyihalarda CI'da manifest cache'ni saqlab qolish muhim.

> **📊 Real raqamlar:** Yuqoridagi uch o'lchov — mening shu sessiyamdagi real `tuist generate --no-open` yugurishlarim (M4 Pro, Tuist 4.211.1, Xcode 27.1). "Issiq" o'lchov uchun generate'ni uch marta yurgizib, birinchisini (build-slot kutishi aralashgan) tashlab, qolgan ikkitasining medianini oldim.

### 11.4 Implicit dependency'ni ushlash (enforceExplicitDependencies o'rniga)

8-bo'limda aytdik: `enforceExplicitDependencies` deprecated, o'rniga `tuist inspect dependencies --only implicit`. Keling, buni amalda ko'raylik.

Toza loyihada buyruq hech narsa topmaydi:

```bash
tuist inspect dependencies --only implicit
```

```text
We did not find any dependency issues in your project (checked: implicit).
```

Endi men ataylab muammo yaratdim: `BooksKit` ichida yangi `Analytics` framework'ni `import` qildim, lekin uni `BooksKit` ning `dependencies` ro'yxatiga **qo'shmadim**. `tuist generate` baribir muvaffaqiyatli o'tdi (generate kodni kompilyatsiya qilmaydi), lekin inspect buyrug'i xatoni ushladi:

```text
✖ Error
  The following implicit dependencies were found:
   - BooksKit implicitly depends on: Analytics
```

Buyruq nol bo'lmagan exit kod bilan chiqadi — demak uni CI'ga qo'ysangiz, implicit dependency kirib qolsa, pipeline qizil bo'ladi. Yechim oddiy: `BooksKit` ning `dependencies` iga `.target(name: "Analytics")` ni qo'shish.

> **💡 Pro tip:** Nega implicit dependency yomon? Xcode ba'zan target `import` qilgan, lekin e'lon qilmagan framework'ni ham topib qo'yadi (chunki hamma product bitta papkada bo'ladi). Bu "ishlab turgandek" ko'rinadi, lekin keyinroq incremental build'ni ishonchsiz qiladi va preview/autocomplete'ni buzadi. Rasmiy hujjat buyruqni **CI'ning har bir push'ida** yurgizishni tavsiya qiladi. Ogohlantirish: Tuist statik tahlilga tayanadi, shuning uchun compiler direktivasi (`#if`) ichidagi shartli import'larni ushlamasligi mumkin.

---

## 12. To'liq misol: noldan generate → build → test {#toliq-misol}

Endi hamma narsani bitta ishlaydigan loyihada birlashtiramiz. Loyiha — **Kutubxona** (kitoblar ro'yxati): ikkita modul (`BooksApp` ilova + `BooksKit` static framework), ikkita configuration, xcconfig fayllari, resource'lar, custom scheme va Swift Testing testlari. Men bu loyihani boshidan oxirigacha o'zim yurgizdim.

### 12.1 Papka tuzilmasi

```text
Kutubxona/
├── Tuist.swift
├── Project.swift
├── Config/
│   ├── Debug.xcconfig
│   └── Release.xcconfig
├── Tuist/
│   └── Package.swift
├── BooksApp/
│   ├── Sources/
│   │   ├── BooksApp.swift
│   │   └── ContentView.swift
│   └── Resources/
│       ├── books.json
│       └── Assets.xcassets/
│           ├── AppIcon.appiconset/
│           ├── AccentColor.colorset/
│           └── Contents.json
└── BooksKit/
    ├── Sources/
    │   └── Book.swift
    └── Tests/
        └── BookTests.swift
```

### 12.2 Uch manifest

```swift
// Tuist.swift
import ProjectDescription

let tuist = Tuist(project: .tuist())
```

```swift
// Project.swift
import ProjectDescription

let project = Project(
    name: "Kutubxona",
    organizationName: "uz.azimjondev",
    options: .options(
        defaultKnownRegions: ["en", "uz"],
        developmentRegion: "uz"
    ),
    settings: .settings(
        base: [
            "SWIFT_VERSION": "6.0",
            "IPHONEOS_DEPLOYMENT_TARGET": "17.0",
        ],
        configurations: [
            .debug(name: "Debug", xcconfig: "Config/Debug.xcconfig"),
            .release(name: "Release", xcconfig: "Config/Release.xcconfig"),
        ],
        defaultConfiguration: "Debug"
    ),
    targets: [
        .target(
            name: "BooksApp",
            destinations: .iOS,
            product: .app,
            bundleId: "uz.azimjondev.kutubxona",
            deploymentTargets: .iOS("17.0"),
            infoPlist: .extendingDefault(with: [
                "UILaunchScreen": [:],
                "CFBundleDisplayName": "Kutubxona",
            ]),
            buildableFolders: [
                "BooksApp/Sources",
                "BooksApp/Resources",
            ],
            dependencies: [
                .target(name: "BooksKit"),
            ]
        ),
        .target(
            name: "BooksKit",
            destinations: .iOS,
            product: .staticFramework,
            bundleId: "uz.azimjondev.kutubxona.kit",
            deploymentTargets: .iOS("17.0"),
            buildableFolders: [
                "BooksKit/Sources",
            ]
        ),
        .target(
            name: "BooksKitTests",
            destinations: .iOS,
            product: .unitTests,
            bundleId: "uz.azimjondev.kutubxona.kit.tests",
            deploymentTargets: .iOS("17.0"),
            buildableFolders: [
                "BooksKit/Tests",
            ],
            dependencies: [
                .target(name: "BooksKit"),
            ]
        ),
    ],
    schemes: [
        .scheme(
            name: "Kutubxona",
            shared: true,
            buildAction: .buildAction(targets: ["BooksApp"]),
            testAction: .targets(["BooksKitTests"]),
            runAction: .runAction(configuration: .debug)
        ),
    ]
)
```

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

#if TUIST
    import ProjectDescription

    let packageSettings = PackageSettings(
        productTypes: [:],
        baseProductType: .staticFramework
    )
#endif

let package = Package(
    name: "Kutubxona",
    dependencies: []
)
```

### 12.3 Kod va resource

```swift
// BooksKit/Sources/Book.swift
import Foundation

public struct Book: Identifiable, Equatable, Sendable {
    public let id: UUID
    public let title: String
    public let author: String

    public init(id: UUID = UUID(), title: String, author: String) {
        self.id = id
        self.title = title
        self.author = author
    }
}

public enum Library {
    public static let sampleBooks: [Book] = [
        Book(title: "O'tkan kunlar", author: "Abdulla Qodiriy"),
        Book(title: "Sariq devni minib", author: "Xudoyberdi To'xtaboyev"),
        Book(title: "Mehrobdan chayon", author: "Abdulla Qodiriy"),
    ]
}
```

```swift
// BooksApp/Sources/BooksApp.swift
import BooksKit
import SwiftUI

@main
struct BooksApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

```swift
// BooksApp/Sources/ContentView.swift
import BooksKit
import SwiftUI

struct ContentView: View {
    private let books = Library.sampleBooks

    var body: some View {
        NavigationStack {
            List(books) { book in
                VStack(alignment: .leading, spacing: 4) {
                    Text(book.title).font(.headline)
                    Text(book.author)
                        .font(.subheadline)
                        .foregroundStyle(.secondary)
                }
            }
            .navigationTitle("Kutubxona")
        }
    }
}
```

```swift
// BooksKit/Tests/BookTests.swift — Swift Testing
import Testing
@testable import BooksKit

struct BookTests {
    @Test func sampleLibraryHasThreeBooks() {
        #expect(Library.sampleBooks.count == 3)
    }

    @Test func bookStoresTitleAndAuthor() {
        let book = Book(title: "Shum bola", author: "G'afur G'ulom")
        #expect(book.title == "Shum bola")
        #expect(book.author == "G'afur G'ulom")
    }
}
```

### 12.4 Generate → build → test

```bash
# 1. Generate
tuist generate --no-open
```

```text
Loading and constructing the graph
It might take a while if the cache is empty
Generating workspace Kutubxona.xcworkspace
Generating project Kutubxona
Total time taken: 1.408s
✔ Success
  Project generated.
```

```bash
# 2. Build (ishlab chiqarilgan workspace'ni xcodebuild bilan)
xcodebuild -workspace Kutubxona.xcworkspace -scheme BooksApp \
  -destination 'generic/platform=iOS Simulator' build
```

```text
** BUILD SUCCEEDED **
```

```bash
# 3. Test (tuist test — custom "Kutubxona" scheme'ini)
tuist test Kutubxona --device "iPhone 17" --os 27.0
```

```text
Suite BookTests started
    ✔ sampleLibraryHasThreeBooks() (0.001 seconds)
    ✔ bookStoresTitleAndAuthor() (0.001 seconds)
Suite BookTests passed after 0.001 seconds
Test Succeeded
✔ Success
  The project tests ran successfully
```

> **🚀 Motivatsiya:** E'tibor bering — biz bitta ham `.xcodeproj` faylga qo'l tegizmadik, bitta ham UUID yozmadik. Butun loyiha — o'qiladigan, review bo'ladigan Swift kodi. Yangi dasturchi `Project.swift` ni ochib, 30 soniyada loyiha tuzilmasini tushunadi. Mana shu — manifest-asosidagi loyihaning "superpower"i.

### 12.5 Men duch kelgan real xato: asset catalog

Birinchi urinishimda build **muvaffaqiyatsiz** bo'ldi. Men `Assets.xcassets` ni atigi bo'sh `Contents.json` bilan yaratgan edim. Xato:

```text
error: None of the input catalogs contained a matching stickers icon set,
app icon set, or icon stack named "AppIcon".
```

Sabab: Tuist default build setting'da `--app-icon AppIcon --accent-color AccentColor` ni uzatadi, lekin mening asset catalog'imda `AppIcon` to'plami yo'q edi. **Yechim** (men qo'llagan): catalog ichiga to'g'ri strukturalangan `AppIcon.appiconset/Contents.json` va `AccentColor.colorset/Contents.json` qo'shdim:

```json
// BooksApp/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
{
  "images": [{ "idiom": "universal", "platform": "ios", "size": "1024x1024" }],
  "info": { "author": "xcode", "version": 1 }
}
```

Shundan keyin build **SUCCEEDED** bo'ldi. Saboq: iOS `.app` target'ida asset catalog bo'lsa, u ichida `AppIcon` to'plami bo'lishi kerak — aks holda `actool` (asset compiler) build'ni to'xtatadi. Tuist bilan `tuist init` yangi loyihada buni avtomatik to'g'ri qo'yadi; biz esa manifestni qo'lda yozganimiz uchun o'zimiz qo'shishimiz kerak bo'ldi.

---

## Amaliy mashqlar {#mashqlar}

1. **Minimal loyiha.** Bo'sh papkada faqat `Tuist.swift` (`let tuist = Tuist(project: .tuist())`) va bitta target'li `Project.swift` yozing. `tuist generate --no-open` qiling. `.xcworkspace` ochilganini tekshiring.

2. **globs → buildableFolders.** `sources: ["App/Sources/**"]` bilan loyiha yozing, generate qiling. Keyin uni `buildableFolders: ["App/Sources"]` ga o'zgartiring va qayta generate qiling. `tuist dump project` chiqishida farqni ko'ring (`"sources"` vs `"buildableFolders"`).

3. **Ikki configuration + xcconfig.** Loyihaga `Debug` va `Release` configuration'lar qo'shing, har biriga `.xcconfig` fayl bog'lang. `tuist dump project` da `"xcconfig"` yo'llari to'g'ri ko'rinayotganini tasdiqlang.

4. **Custom scheme.** `schemes:` ga o'z scheme'ingizni yozing (build + test action bilan), `shared: true` qiling. `xcodebuild -workspace <nom>.xcworkspace -list` bilan scheme ro'yxatida paydo bo'lganini tekshiring.

5. **Implicit dependency'ni ushlash ⭐.** Ikki modulli loyiha yozing; bir modulda ikkinchisini `import` qiling, lekin `dependencies` ga **qo'shmang**. `tuist inspect dependencies --only implicit` yurgizing — xato chiqishini ko'ring. Keyin bog'liqlikni qo'shib, buyruq toza o'tganini tasdiqlang.

6. **Workspace ⭐.** Ikkita project'ni (`App` va `Feature`) bitta `Workspace.swift` ga yig'ing; App'ni Feature'ga `.project(target:path:)` bilan bog'lang. Generate qilib, workspace'da ikkala project ham ko'ringanini tekshiring.

---

## O'zingizni tekshiring {#checklist}

- [ ] `import ProjectDescription` har bir manifestning birinchi qatori ekanini va nega kerakligini tushunaman.
- [ ] `Project.swift` ildizida faqat bitta `let project = Project(...)` bo'lishi kerakligini bilaman.
- [ ] `destinations` (qurilma oilalari) va `deploymentTargets` (minimal OS versiyasi) farqini ayta olaman.
- [ ] `buildableFolders` va `sources`/`resources` globs orasidagi farqni va qachon qaysi birini tanlashni bilaman.
- [ ] `.settings(base:configurations:)` bilan xcconfig'li ikki configuration yoza olaman.
- [ ] Avtomatik scheme'lar qanday hosil bo'lishini va test target o'z scheme'iga biriktirilishini bilaman.
- [ ] `Tuist.swift` ning 2026 shakli `Tuist(project: .tuist(...))` ekanini va eski init deprecated ekanini bilaman.
- [ ] `enforceExplicitDependencies` o'rniga `tuist inspect dependencies --only implicit` ishlatilishini bilaman.
- [ ] Tashqi paketlar `Tuist/Package.swift` da e'lon qilinib, `Tuist/.build` ga yechilishini bilaman.
- [ ] `tuist edit`, `tuist dump` va manifest caching nima qilishini tushunaman.

---

## Xulosa {#xulosa}

- Tuist loyihasi **Swift manifestlar** bilan ta'riflanadi; `.xcodeproj` esa generate qilinadigan natija — Git'ga tushmaydi, merge conflict bermaydi.
- To'rtta manifest: `Tuist.swift` (root + global config), `Project.swift` (bitta project), `Workspace.swift` (ko'p project), `Tuist/Package.swift` (tashqi paketlar). Hammasi `import ProjectDescription` bilan boshlanadi.
- `Target` — bitta build mahsuloti; eng muhim parametrlar `destinations`, `deploymentTargets`, `product`, `bundleId`, `buildableFolders`/`sources`, `dependencies`.
- 2026 yangiligi: **`buildableFolders`** (Xcode 16 synchronized groups) fayl qo'shilganda qayta generate ehtiyojini yo'qotadi.
- **`Tuist.swift` ning to'g'ri 2026 shakli** `Tuist(project: .tuist(...))`; eski `Tuist(generationOptions:...)` va `enforceExplicitDependencies` — deprecated. Implicit dependency endi `tuist inspect dependencies --only implicit` bilan ushlanadi.
- `tuist dump` manifestingiz nimaga aylanganini aniq ko'rsatadi; manifest caching birinchi generate'ni sekinroq, keyingilarini ~0.6 s qiladi (M4 Pro'da o'lchangan).
- Hamma narsa hisobsiz, lokalda ishlaydi — biz to'liq loyihani generate → build → test qildik, bironta ham Tuist hisobi talab qilinmadi.
- **Keyingi dars:** [03. Papka tuzilmasi va CLI — har bir buyruq 2026 holatida, real natijalar bilan](/ios-lessons/tuist-03-directory-structure)

---

## Manbalar {#manbalar}

1. [Tuist — Manifests (Project.swift, Workspace.swift, Tuist.swift)](https://tuist.dev/en/docs/guides/features/projects/manifests)
2. [Tuist — Editing (`tuist edit`)](https://tuist.dev/en/docs/guides/features/projects/editing)
3. [Tuist — Dependencies (`Tuist/Package.swift`, PackageSettings)](https://tuist.dev/en/docs/guides/features/projects/dependencies)
4. [Tuist — Code sharing (ProjectDescriptionHelpers)](https://tuist.dev/en/docs/guides/features/projects/code-sharing)
5. [Tuist — Inspect implicit imports (`tuist inspect dependencies`)](https://tuist.dev/en/docs/guides/features/projects/inspect/implicit-dependencies)
6. [Tuist changelog — Buildable folders (2025-08-11, #7984)](https://tuist.dev/changelog/2025.08.11-buildable-folders)
7. [ProjectDescription manbasi — Target.swift (manifest API)](https://github.com/tuist/tuist/blob/main/cli/Sources/ProjectDescription/Target.swift)
8. [Tuist — v3 dan v4 ga migratsiya (Config.swift → Tuist.swift, tuist fetch → install)](https://tuist.dev/en/docs/references/migrations/from-v3-to-v4)
9. [ProjectDescription reference (Project, Settings, Scheme, BuildableFolder...)](https://projectdescription.tuist.dev/documentation/projectdescription)
10. [Asana Engineering — Scaling a mature iOS codebase with Tuist](https://asana.com/inside-asana/scaling-a-mature-ios-codebase-with-tuist)
