# Bazel 9: Xcode bilan ishlash — rules_xcodeproj, indexing, debugging, SwiftUI Preview va VS Code

> **Holat:** 2026-yil oktabr. Bu darsda Bazel bilan build qiladigan iOS loyihasini Xcode IDE ichida ochib, indexing, debugging va test navigator'dan foydalanamiz — `rules_xcodeproj` orqali. Kod misollari Bazel 9.3.0, rules_apple 5.3.0, rules_swift 4.2.1, rules_xcodeproj 4.1.0 va Xcode 27.1 bilan Apple M4 Pro'da tekshirilgan.

> **Bu darsdan keyin siz:** Bazel loyihasi uchun `bazel run //:xcodeproj` bilan haqiqiy `.xcodeproj` yaratib, uni Xcode'da yoki VS Code'da ochib, kod yozish va debug qilishni biladigan bo'lasiz.

- `rules_xcodeproj` nega kerakligini va Bazel'ni iOS'da qabul qilishdagi **#1 to'siq** aynan IDE/DX ekanini tushunasiz (Asana'ning real hikoyasi bilan).
- `xcodeproj` qoidasini (`project_name`, `top_level_targets`, `top_level_target`, `focused_targets`) yozib, loyihani generatsiya qilasiz.
- Generatsiya qilingan loyihaning **"Build with Bazel" (BwB)** modeli qanday ishlashini — `BazelDependencies` target va "Generate Bazel Dependencies" script phase'ni — ko'rasiz.
- Indexing, breakpoint/LLDB va test navigator'dan foydalanasiz; SwiftUI Preview'ning 4.1.0 dagi real holatini (va cheklovini) bilasiz.
- Alternativa sifatida VS Code / Cursor'ni `sourcekit-lsp` + Build Server Protocol orqali Bazel loyihasiga ulashni ko'rasiz.

## Mundarija

1. [Muammo: IDE yo'q bo'lsa, Bazel qabul qilinmaydi](#muammo)
2. [rules_xcodeproj nima qiladi: "Build with Bazel" modeli](#model)
3. [O'rnatish va birinchi loyiha generatsiyasi](#ornatish)
4. [Generatsiya qilingan loyihani CLI'dan build qilish (BwB)](#build)
5. [Indexing, debugging va test navigator](#indexing)
6. [SwiftUI Preview: eng katta kamchilik (halol gap)](#preview)
7. [.bazelrc config oilasi va command-line API](#configlar)
8. [Focused projects: katta repo uchun](#focused)
9. [Katta loyihaga xos muammolar va signing](#muammolar)
10. [Alternativa: VS Code / Cursor — sourcekit-lsp + BSP](#vscode)
11. [Amaliy mashqlar](#mashqlar)
12. [O'zingizni tekshiring](#checklist)
13. [Xulosa](#xulosa)
14. [Manbalar](#manbalar)

---

## 1. Muammo: IDE yo'q bo'lsa, Bazel qabul qilinmaydi {#muammo}

Oldingi darslarda ([Bazel 5](/ios-lessons/bazel-05-ios-ilova), [Bazel 6](/ios-lessons/bazel-06-testlar), [Bazel 8](/ios-lessons/bazel-08-ilova-anatomiyasi)) biz hamma narsani terminaldan qildik: `bazel build`, `bazel test`, `bazel run`. Bu CI uchun ajoyib. Lekin kuniga 8 soat kod yozadigan iOS dasturchi uchun terminal yetarli emas. Unga kerak:

- **Autocomplete** va **jump to definition** — `BookCatalog.search(` deb yozganda Xcode qaysi parametrlarni kutayotganini ko'rsatishi;
- **Breakpoint** qo'yib, `po book.title` deb o'zgaruvchini tekshirish (LLDB);
- **Inline xatolar** — kompilyatsiya xatosi aynan qaysi qatorda ekanini qizil chiziq bilan ko'rsatishi;
- **Test navigator** — testni bosib ishga tushirish, yashil/qizil belgilar;
- **SwiftUI Preview** — `#Preview` bloki canvas'da jonli ko'rinishi.

Bu ro'yxat — Bazel'ning emas, **Xcode'ning** super-kuchlari. Bazel faqat build tizimi: u fayllarni kompilyatsiya qiladi, lekin IDE emas. Shuning uchun Bazel'ni iOS jamoasiga olib kirishdagi eng katta to'siq — tezlik emas, balki **IDE/developer experience (DX)**.

> **🏢 Real-world:** Asana muhandislik jamoasi (2023-yil 21-fevral, "Engineering Team" posti) ~10 muhandis va ~300 000 qator Swift kodini modullarga ajratishdan oldin build tizimini tanlagan. Ular **Bazel'dan voz kechib, Tuist'ni tanlagan**. Sababi — tezlik emas, DX edi: Bazel ular uchun "juda og'ir (very heavyweight) va yangi mental model talab qiladigan" vosita bo'lgan, eng muhimi — "**breakpoint'lar bilan ish jarayonini buzadigan muammolar**" bo'lgan. Modullarga ajratgandan keyin o'zgarishdan-simulyatorgacha vaqt odatdagi ~45 soniyadan Scalyr'dagi median ~15 soniyagacha tushgan, Inbox moduli esa 30–50 soniyadan ~4 soniyagacha. Manba: [asana.com/inside-asana](https://asana.com/inside-asana/scaling-a-mature-ios-codebase-with-tuist).

Bu hikoyaning darsimizga aloqasi aniq: agar Bazel loyihasini Xcode'da **oddiy loyiha kabi** ochib, breakpoint qo'yib, kod to'ldirib ishlata olsangiz — Bazel'ning asosiy kamchiligi yo'qoladi. Aynan shu muammoni `rules_xcodeproj` hal qiladi.

`rules_xcodeproj` — Bazel target'laridan haqiqiy `.xcodeproj` fayl generatsiya qiladigan qoidalar to'plami. U [Mobile Native Foundation](https://github.com/MobileNativeFoundation/rules_xcodeproj) ostida yuritiladi (dastlab BuildBuddy tomonidan yaratilib, keyin MNF'ga topshirilgan), g'oyasi Google'ning Tulsi'si va Target/Lyft'ning ichki generatorlaridan kelib chiqqan. README'da foydalanuvchi sifatida 19 ta loyiha sanab o'tilgan: **Cash App, Lyft, Reddit, Robinhood, Slack, Snap, Spotify, Square, Tinder, Gojek, Mercari, Faire, Envoy Mobile, SwiftLint, amo, BazelPods, Ergatta, Ten Ten, Tokopedia** (manba: `rules_xcodeproj` README, 4.1.0).

> **🚀 Motivatsiya:** Bir buyruq — `bazel run //:xcodeproj` — va sizda Bazel build qiladigan, lekin Xcode'da xuddi oddiy loyihadek ochiladigan `.app` loyihasi paydo bo'ladi. Dasturchi Bazel borligini deyarli sezmaydi: u Cmd+R bosadi, breakpoint qo'yadi, kodni to'ldiradi. Jamoangiz CI'da Bazel cache'dan foyda ko'radi, dasturchilar esa odatdagi Xcode'da qoladi.

---

## 2. rules_xcodeproj nima qiladi: "Build with Bazel" modeli {#model}

Oddiy Xcode loyihasida **Xcode'ning o'z build tizimi** (XCBBuildService) kodni kompilyatsiya qiladi. `rules_xcodeproj` generatsiya qilgan loyihada esa boshqacha: Xcode faqat **muharrir va ishga tushiruvchi** bo'lib qoladi, haqiqiy build'ni esa **Bazel** bajaradi. Bu model **"Build with Bazel" (BwB)** deb ataladi.

Mexanizm shunday ishlaydi: generatsiya qilingan loyihada `BazelDependencies` degan maxsus target va har bir build'da ishga tushadigan "Generate Bazel Dependencies" nomli **Run Script build phase** bo'ladi. Xcode "build" tugmasini bosganingizda, bu skript `bazel build`ni chaqiradi; Bazel haqiqiy ishni qiladi va natijani `bazel-out/`ga qo'yadi; keyin Xcode o'zining build qadamlarini "stub" qilib (bo'sh o'tkazib) natijani oladi. Xcode'ning serialized diagnostics (ogohlantirish/xato) ma'lumotlari qayta o'ynatiladi, shuning uchun inline xatolar va fix-it'lar ishlaydi.

![rules_xcodeproj "Build with Bazel" oqimi: Xcode muharrir va BazelDependencies stub, haqiqiy build'ni Bazel bajaradi, natija bazel-out'dan keladi](/lessons/images/bazel/bazel_09_rules_xcodeproj_flow.svg)

Diagrammada: siz Xcode'da Cmd+B bosasiz → `BazelDependencies` script phase `bazel build`ni chaqiradi → Bazel action graph'ni hisoblab, faqat o'zgargan narsani kompilyatsiya qiladi (cache'dan foydalanib) → natija `bazel-out/`ga tushadi → Xcode uni DerivedData'ga nusxalaydi va ishga tushiradi. Indexing va debugging Xcode tomonda qoladi.

**Nega bu yaxshi?** Chunki Xcode ichidagi build ham, CI'dagi `bazel build` ham **bir xil Bazel action'lari**ni ishlatadi. Ya'ni ularning cache'i umumiy: CI "warm" qilgan cache'dan dasturchining Xcode'i ham foyda ko'rishi mumkin (buni [Bazel 10](/ios-lessons/bazel-10-cache) va [Bazel 11](/ios-lessons/bazel-11-ci-cd) darslarida ko'ramiz). Oddiy Xcode build tizimi bunday umumiy cache'ni bermaydi.

> **🧠 Esda tuting:** 4.x versiyada `xcodeproj` qoidasida `build_mode` atributi **yo'q**. Eski versiyalarda "Build with Xcode" (BwX) — Bazel target'larini Xcode'ning o'z build tizimi orqali qurish — rejimi ham bor edi, lekin 3.0.0 (2025-06-13) "legacy generator" ni olib tashladi va 4.1.0 manba kodida `build_mode` so'zi faqat `CHANGELOG.md`da uchraydi. Demak hozir **BwB — yagona rejim**. (`docs/faq.md`da hali ham eski BwX haqida matn qolgan — bu eskirgan, unga ishonmang.)

> **⚠️ Diqqat:** `rules_xcodeproj` loyihangiz build qilish usulini **o'zgartirmaydi**. Uning falsafasi (design-goals.md): "Xcode xususiyatini yoqish uchun target build qilinishini o'zgartirmaymiz." Ya'ni u Bazel graph'ingiz ustiga Xcode qatlamini qo'yadi, xolos. Bu — kuch (barcha target'lar bir xil quriladi), lekin ayni paytda cheklov ham (Xcode'ning ba'zi avtomatik xususiyatlari, masalan SwiftUI Preview, oson ishlamaydi — 6-bo'limga qarang).

---

## 3. O'rnatish va birinchi loyiha generatsiyasi {#ornatish}

Biz [Bazel 5](/ios-lessons/bazel-05-ios-ilova) darsidagi "Kitoblar" ilovasini qayta ishlatamiz: `Core` moduli (kitob modeli + qidiruv mantig'i) va `App` moduli (SwiftUI). Loyiha tuzilishi:

```text
kitoblar/
├── .bazelversion
├── .bazelrc
├── MODULE.bazel
├── BUILD.bazel            ← xcodeproj target shu yerda
├── Core/
│   ├── BUILD.bazel
│   ├── Sources/
│   │   ├── Book.swift
│   │   └── BookCatalog.swift
│   └── Tests/
│       └── BookCatalogTests.swift
└── App/
    ├── BUILD.bazel
    ├── Info.plist
    └── Sources/
        ├── KitoblarApp.swift
        └── BookListView.swift
```

### 3.1. MODULE.bazel'ga rules_xcodeproj'ni qo'shish

`rules_xcodeproj`ni boshqa dependency'lar qatorida `bazel_dep` bilan qo'shamiz. U faqat dasturchining mashinasida, loyiha generatsiya qilishda kerak — CI'dagi `bazel build`ga ta'sir qilmaydi.

```starlark
# MODULE.bazel
module(name = "kitoblar", version = "1.0.0")

# apple_support rules_cc'dan yuqorida turishi kerak — birinchi ro'yxatdan
# o'tgan toolchain yutadi.
bazel_dep(name = "apple_support", version = "2.10.1")
bazel_dep(name = "rules_swift", version = "4.2.1")
bazel_dep(name = "rules_apple", version = "5.3.0")

# Xcode loyihasini generatsiya qilish uchun.
bazel_dep(name = "rules_xcodeproj", version = "4.1.0")
```

> **⚠️ Diqqat — mosligini o'zingiz tekshiring:** `rules_xcodeproj` 4.1.0 — 2026-05-07 da chiqqan oxirgi relizi (o'shandan beri 5 oy yangilanish yo'q). Uning README'dagi "Compatibility" jadvali **eskirgan**: u hali ham Bazel 7.0–9.x, Xcode 13.3–16.x, macOS 13–15.x deb yozadi va "rasman faqat shular bilan sinaldi" deydi. 4.x CHANGELOG'da Xcode 26/27 umuman tilga olinmagan. Shuning uchun biz "4.1.0 Xcode 27.1 bilan rasman mos" deb da'vo qilmaymiz — buni **o'zimiz tekshiramiz** (quyida). Siz ham o'z Xcode versiyangizda tekshiring.

### 3.2. xcodeproj target'ini yozish

Ildiz `BUILD.bazel` fayliga `xcodeproj` qoidasini qo'shamiz. Eng kam konfiguratsiya — `name`, `project_name` va `top_level_targets`:

```starlark
# BUILD.bazel
load("@rules_xcodeproj//xcodeproj:defs.bzl", "top_level_target", "xcodeproj")

xcodeproj(
    name = "xcodeproj",
    project_name = "Kitoblar",
    # Bazel bu targetni "//..." ichida tasodifan build qilmasligi uchun.
    tags = ["manual"],
    top_level_targets = [
        top_level_target("//App:Kitoblar", target_environments = ["simulator"]),
        "//Core:CoreTests",
    ],
)
```

Bu yerda:

- `top_level_targets` — Xcode'da "asosiy" bo'lib ko'rinadigan target'lar ro'yxati. Ularning barcha transitive dependency'lari ham loyihaga tushadi. Bizda ilova (`//App:Kitoblar`) va test (`//Core:CoreTests`).
- `top_level_target(label, target_environments = [...])` — target'ni qaysi muhit uchun sozlashni aytadi. `"simulator"` yoki `"device"` (yoki ikkalasi). Agar `["device", "simulator"]` bersangiz, imkon bo'lsa bitta birlashgan Xcode target yaratiladi. Biz simulyator uchun quramiz.
- `tags = ["manual"]` — `bazel build //...` bu target'ni avtomatik qurmasligi uchun (u dasturchi qo'li bilan ishga tushiriladigan generator).

> **💡 Pro tip:** `project_name = "Kitoblar"` — hosil bo'ladigan fayl nomi (`Kitoblar.xcodeproj`). Agar bermasangiz, `name` (ya'ni `xcodeproj`) ishlatiladi va fayl `xcodeproj.xcodeproj` bo'lib ketadi — buni istamaysiz.

### 3.3. Haqiqiy xato: visibility

Agar `xcodeproj` target ildizda, `//App:Kitoblar` esa boshqa paketda bo'lsa, birinchi urinishda shunday xato chiqishi mumkin:

```text
Error: target '//App:Kitoblar' is not visible from target
'@@rules_xcodeproj++...rules_xcodeproj_generated//generator/xcodeproj:xcodeproj'
```

Sabab — `rules_xcodeproj` ichki ravishda `@rules_xcodeproj_generated` repo'sida yangi paket yaratadi va sizning target'laringizga murojaat qiladi. FAQ aytganidek, siz murojaat qiladigan target'lar shu paketni ko'ra olishi kerak. Yechim — `visibility`ga maxsus guruhni qo'shish:

```starlark
# Core/BUILD.bazel
ios_unit_test(
    name = "CoreTests",
    minimum_os_version = "17.0",
    deps = [":CoreTestsLib"],
    # xcodeproj generatsiya qilgan paket shu targetni ko'ra olishi uchun.
    visibility = ["@rules_xcodeproj//xcodeproj:generated"],
)
```

```starlark
# App/BUILD.bazel
ios_application(
    name = "Kitoblar",
    bundle_id = "uz.azimjondev.kitoblar",
    families = ["iphone"],
    infoplists = ["Info.plist"],
    minimum_os_version = "17.0",
    deps = [":AppLib"],
    visibility = ["@rules_xcodeproj//xcodeproj:generated"],
)
```

> **🧠 Esda tuting:** `"@rules_xcodeproj//xcodeproj:generated"` — bu `rules_xcodeproj`ning rasmiy `package_group`'i. Agar barcha target'lar bitta `BUILD.bazel`da bo'lsa (README'dagi oddiy misoldagi kabi), bu kerak emas. Lekin haqiqiy loyihalar ko'p paketli, shuning uchun bu xatoga albatta duch kelasiz.

### 3.4. Generatsiya: bazel run //:xcodeproj

Endi loyihani generatsiya qilamiz:

```bash
bazel run //:xcodeproj
```

Birinchi marta bu buyruq uzoq ishlaydi — chunki Bazel `rules_xcodeproj`ning generator vositasini (Swift'da yozilgan) va u bog'liq bo'lgan kutubxonalarni (jumladan, 4.1.0 bilan birga keladigan rsync'ni) noldan kompilyatsiya qiladi. Bizning o'lchovimiz:

```text
INFO: Elapsed time: 181.209s, Critical Path: 11.49s
INFO: 1481 processes: 4 disk cache hit, 158 internal, 1319 local.
INFO: Build completed successfully, 1481 total action
INFO: Running command line: .../rules_xcodeproj.noindex/build_output_base/.../xcodeproj-installer.sh
Updated project at "Kitoblar.xcodeproj"
```

**Natija:** `Updated project at "Kitoblar.xcodeproj"` — loyiha hosil bo'ldi! (Apple M4 Pro, Xcode 27.1, bo'sh output base. Bu 181 soniya — **bir martalik narx**: generator keshga tushgach, keyingi generatsiyalar bir necha soniya.)

> **📊 Real raqamlar:** `BUILD.bazel`ga bitta qator qo'shib (yangi fayl/target qo'shishni taqlid qilib) qayta generatsiya qilganda — generator allaqachon kompilyatsiya qilingani uchun — bizda **8.75 soniya** (`real 8.75`) ketdi. Ya'ni kundalik "BUILD o'zgartirdim → qayta generatsiya" aylanmasi arzon.

### 3.5. Loyihada nimalar paydo bo'ldi

Generatsiyadan keyin ildizda `Kitoblar.xcodeproj` paydo bo'ladi. Uning ichida (eslatma: aksariyat fayllar `rules_xcodeproj/` ichida — bu normal):

```text
Kitoblar.xcodeproj/
├── project.pbxproj
├── project.xcworkspace/
├── xcshareddata/xcschemes/
│   ├── Core.xcscheme
│   ├── CoreTests.xcscheme
│   └── Kitoblar.xcscheme
├── rules_xcodeproj/
│   └── bazel/
│       ├── xcodeproj.bazelrc          ← config oilasi shu yerda (7-bo'lim)
│       └── ... (BwB uchun skriptlar)
└── xcuserdata/
```

`xcodebuild -list` bilan tekshirsak (bizning haqiqiy natijamiz):

```text
    Targets:
        BazelDependencies    ← BwB "ko'prigi" (stub target)
        Core
        CoreTests
        Kitoblar
    Schemes:
        Core
        CoreTests
        Kitoblar
```

`BazelDependencies` — biz 2-bo'limda aytgan BwB target. Qolgan uchtasi — sizning target'laringiz. Har bir "buildable" target uchun avtomatik **scheme** yaratildi (bu `scheme_autogeneration_mode = "auto"` standart xatti-harakati).

Loyihaning Swift kodi oddiy (bu [Bazel 5](/ios-lessons/bazel-05-ios-ilova)dan):

```swift
// Core/Sources/BookCatalog.swift
public struct BookCatalog: Sendable {
    public private(set) var books: [Book]

    public init(books: [Book] = BookCatalog.sample) {
        self.books = books
    }

    public func search(_ query: String) -> [Book] {
        guard !query.isEmpty else { return books }
        let q = query.lowercased()
        return books.filter {
            $0.title.lowercased().contains(q) || $0.author.lowercased().contains(q)
        }
    }
    // ... (to'liq versiya loyihada)
}
```

Va `swift_library` BUILD bo'lagi:

```starlark
# Core/BUILD.bazel
load("@rules_swift//swift:swift.bzl", "swift_library")
load("@rules_apple//apple:ios.bzl", "ios_unit_test")

swift_library(
    name = "Core",
    srcs = glob(["Sources/**/*.swift"]),
    module_name = "Core",
    visibility = ["//visibility:public"],
)
```

---

## 4. Generatsiya qilingan loyihani CLI'dan build qilish (BwB) {#build}

Odatda loyihani Xcode'da (`open Kitoblar.xcodeproj`) ochib, Cmd+B bosasiz. Lekin biz avval terminalda tekshiramiz — bu CI uchun ham muhim va "haqiqatan ishladi" degan dalil beradi. `xcodebuild`ni ishlatamiz:

```bash
xcodebuild \
  -project Kitoblar.xcodeproj \
  -scheme Kitoblar \
  -destination 'generic/platform=iOS Simulator' \
  -derivedDataPath .dd \
  build
```

Natijaning oxiri (bizning haqiqiy chiqishimiz, Xcode 27.1):

```text
note: Run script build phase 'Generate Bazel Dependencies' will be run during
every build because the option to run the script phase "Based on dependency
analysis" is unchecked. (in target 'BazelDependencies' from project 'Kitoblar')
note: Run script build phase 'Copy Bazel Outputs / Generate Bazel Dependencies
(Index Build)' will be run during every build ... (in target 'Kitoblar' ...)
** BUILD SUCCEEDED **
```

**`** BUILD SUCCEEDED **`** — generatsiya qilingan loyiha Xcode 27.1 da muvaffaqiyatli quriladi! Chiqishda BwB mexanizmini aniq ko'ramiz:

- `'Generate Bazel Dependencies'` script phase — `BazelDependencies` target ichida, har build'da ishlaydi (chunki "Based on dependency analysis" o'chirilgan — bu ataylab, Bazel o'zi qaror qiladi);
- `'Copy Bazel Outputs ...'` — Bazel natijalarini Xcode'ning DerivedData'siga ko'chiradi.

App bundle `bazel-out/ios_sim_arm64-dbg-.../bin/App/Kitoblar.app`da paydo bo'ladi — ya'ni haqiqiy ishni Bazel qilgan, Xcode faqat uni o'rab olgan.

> **📊 Real raqamlar (Apple M4 Pro, 12 core, 24 GB; Xcode 27.1; Bazel 9.3.0; rules_xcodeproj 4.1.0):**
>
> | Amal                                               | Vaqt               | Izoh                                                            |
> | -------------------------------------------------- | ------------------ | --------------------------------------------------------------- |
> | `bazel run //:xcodeproj` (birinchi)                | 181.2 s            | generator + rsync'ni noldan kompilyatsiya qiladi (bir martalik) |
> | qayta generatsiya (BUILD o'zgardi)                 | 8.75 s             | generator keshda                                                |
> | `xcodebuild ... build` (birinchi, BwB)             | ✅ BUILD SUCCEEDED | to'liq Bazel build                                              |
> | `xcodebuild ... build` (o'zgarishsiz, incremental) | 1.99 s             | BwB stub tez o'tadi                                             |
> | `xcodebuild` qayta generatsiyadan keyin            | 90.8 s             | DerivedData bekor bo'ldi → to'liq qayta build                   |
>
> Oxirgi qator muhim: **loyihani qayta generatsiya qilsangiz, Xcode'ning DerivedData'si bekor bo'ladi va keyingi build to'liq bo'ladi** (quyida 9-bo'limga qarang).

> **⚠️ Diqqat — har doim `-derivedDataPath` bering:** biz `-derivedDataPath .dd` berdik, ya'ni DerivedData loyiha ichidagi `.dd` papkaga tushadi. Bu — izolyatsiya uchun yaxshi odat (ayniqsa CI'da): umumiy `~/Library/Developer/Xcode/DerivedData`ni ifloslantirmaysiz.

---

## 5. Indexing, debugging va test navigator {#indexing}

Endi `open Kitoblar.xcodeproj` qilib Xcode'da ochamiz. `rules_xcodeproj` README'si va'da qiladigan xususiyatlar: indexing, debugging, runtime sanitizer'lar, inline ogohlantirish/xatolar, test tanlash va ishga tushirish, embedded target'lar (App Clip, extension, Watch), dynamic framework'lar va Xcode Previews. Qaysilari **haqiqatan** ishlaydi? Keling, mexanizmini ko'raylik.

### 5.1. Indexing (autocomplete, jump to definition)

Indexing — Xcode kodingizni "tushunishi": `catalog.` deb yozganingizda metodlar ro'yxati, `BookCatalog`ni Cmd+bosib ta'rifiga o'tish, syntax rang berish. Buning uchun Xcode fayl bo'yicha kompilyatsiya qila olishi kerak.

`rules_xcodeproj` buni **"Index Build"** (Prepare for Indexing) orqali hal qiladi: Xcode fonda loyihani alohida output papkaga quradi, indexing uchun kerakli artefaktlarni tayyorlaydi. Generatsiya qilingan `xcodeproj.bazelrc`da buni ko'rsatuvchi feature'lar bor (bizning fayldan):

```text
common:rules_xcodeproj --features=swift.index_while_building
common:rules_xcodeproj --features=swift.use_global_index_store
common:rules_xcodeproj_indexbuild --config=rules_xcodeproj
common:rules_xcodeproj_indexbuild --bes_backend= --bes_results_url=
```

- `swift.index_while_building` — Swift kompilyatori build paytida index store yozib boradi;
- `swift.use_global_index_store` — index'ni umumiy joyda saqlaydi (qayta ishlatish uchun);
- `rules_xcodeproj_indexbuild` config — Index Build fonda tez-tez ishlagani uchun telemetriyani (BES upload'ni) o'chiradi.

`xcodeproj` qoidasida `import_index_build_indexstores` atributi standart holatda `True` — bu Index Build hosil qilgan index store'larni import qilib, Xcode'ning indekslashini tezlashtiradi. Agar disk IO ko'p bo'lsa va arzimasa, uni `False` qilishingiz mumkin.

> **💡 Pro tip:** birinchi marta loyihani ochganingizda Xcode biroz "o'ylaydi" — bu Index Build. Bir marta tugagach, autocomplete va jump-to-definition oddiy Xcode kabi ishlaydi. Agar indexing "qotib qolsa", Xcode'ni yopib-ochish yoki loyihani qayta generatsiya qilish yordam beradi.

### 5.2. Debugging (breakpoint, LLDB)

`rules_xcodeproj`ning asosiy maqsadi (design-goals.md so'zlari bilan) — "target'larni ishga tushirish va debug qilishni qo'llab-quvvatlash". Esingizda bo'lsa, Asana aynan breakpoint muammosi tufayli Bazel'dan voz kechgan edi — `rules_xcodeproj` esa shu muammoni hal qilish uchun yaratilgan.

Amalda: `BookCatalog.search`ga breakpoint qo'yasiz, Cmd+R bilan ilovani simulyatorda ishga tushirasiz, qidiruv maydoniga "kunlar" yozasiz — Xcode breakpoint'da to'xtaydi, siz `po q` yoki `po books.count` deb LLDB'da tekshirasiz. Bu oddiy Xcode debug tajribasi, farqi — binar'ni Bazel qurgan.

> **🧠 Esda tuting:** BwB rejimida debug ma'lumotlari (`.dSYM`, serialized diagnostics) Bazel tomonidan hosil qilinadi va Xcode'ga uzatiladi. `xcodeproj.bazelrc`dagi `--features=oso_prefix_is_pwd` va `--features=relative_ast_path` — aynan debugging/indexing yo'llarini to'g'ri xaritalash uchun.

### 5.3. Test navigator

`//Core:CoreTests` target uchun avtomatik `CoreTests` scheme yaratilgani uchun, Xcode'ning Test navigator'ida (Cmd+6) testlar ko'rinadi. Bizning testlar:

```swift
// Core/Tests/BookCatalogTests.swift
final class BookCatalogTests: XCTestCase {
    func testSearchByTitle() {
        let catalog = BookCatalog()
        let results = catalog.search("kunlar")
        XCTAssertEqual(results.count, 1)
        XCTAssertEqual(results.first?.author, "Abdulla Qodiriy")
    }

    func testSortByYear() {
        let catalog = BookCatalog()
        XCTAssertEqual(catalog.sorted(by: \.year).first?.year, 1925)
    }
}
```

Test yonidagi olmosni bosib bitta testni, yoki butun klassni ishga tushirasiz. Natija yashil/qizil belgi bilan ko'rinadi. Ichkarida bu `bazel test` bilan bir xil ishni qiladi (test cache haqida [Bazel 6](/ios-lessons/bazel-06-testlar)da).

> **⚠️ Diqqat — coverage va cache:** agar Xcode'da "Gather coverage data" ni yoqsangiz, `rules_xcodeproj_coverage` config ishga tushadi. Hujjat ogohlantiradi: coverage build'lari manba fayllarga **mutlaq (absolute) yo'llar** yozadi, bu esa hermetik emas va **cache hit'larni kamaytiradi**. Tavsiya: coverage'ni Xcode'da faqat faol ishlatganda yoqing; aks holda coverage'ni oddiy `bazel coverage` (CLI) orqali oling.

---

## 6. SwiftUI Preview: eng katta kamchilik (halol gap) {#preview}

`rules_xcodeproj` README'si xususiyatlar ro'yxatida "Xcode Previews"ni sanaydi. Lekin **4.1.0 da SwiftUI Preview — eng katta DX kamchiligi**. Buni ochiq aytamiz, chunki bu ko'p dasturchini hafsalasini pir qiladi.

Bizning `BookListView`da `#Preview` bor:

```swift
// App/Sources/BookListView.swift
#Preview {
    BookListView(catalog: BookCatalog())
}
```

Oddiy Xcode loyihasida bu canvas'da jonli ko'rinardi. `rules_xcodeproj` loyihasida esa — ishlamaydi. Sabab mexanizmda: Xcode 16+ SwiftUI Preview'lari ishlashi uchun `ENABLE_DEBUG_DYLIB = YES` build setting kerak. Lekin `rules_xcodeproj` buni `NO` qilib qo'yadi. Biz buni generatsiya qilingan `project.pbxproj`da **o'z ko'zimiz bilan tekshirdik**:

```bash
grep -o "ENABLE_DEBUG_DYLIB = [A-Z]*" Kitoblar.xcodeproj/project.pbxproj | sort | uniq -c
```

```text
   1 ENABLE_DEBUG_DYLIB = NO
```

Bu `rules_xcodeproj`da ataylab qo'yilgan (`#3042` — "Xcode 16'ni to'g'ri qo'llab-quvvatlagunimizcha"). Natija: `#Preview` bloklari canvas'da render bo'lmaydi.

**Holat (2026-oktabr, tekshirilgan):**

- Issue [#3201](https://github.com/MobileNativeFoundation/rules_xcodeproj/issues/3201) "Feature Request: Xcode SwiftUI Preview Support" — 2025-06-20 da ochilgan, **hali ham ochiq** (oxirgi faollik 2026-09-17).
- Native preview'ni qo'shish uchun urinilgan PR'lar (`#3348` "opt-in native Xcode Preview configurations" (draft), `#3325`) 2026-10-13 holatiga **yopilgan, lekin merge qilinmagan**. Ya'ni 4.1.0 da native Preview chiqmagan.
- `xcodeproj.bazelrc`da `rules_xcodeproj_swiftuipreviews` config bor (u `-enable-implicit-dynamic`, `-enable-private-imports`, `-enable-dynamic-replacement-chaining` copt'larini qo'shadi), lekin `ENABLE_DEBUG_DYLIB = NO` tufayli Xcode 16+/27 da bu to'liq preview tajribasini bermaydi.

> **⚠️ Diqqat:** Agar sizning ishingiz SwiftUI Preview'ga juda bog'liq bo'lsa (masalan, dizayn-og'ir ekranlar ustida ishlayotgan bo'lsangiz), Bazel + rules_xcodeproj 4.1.0 bu borada sizni cheklaydi. Amaldagi yechimlar: (1) ilovani simulyatorda tez-tez ishga tushirish (hot reload o'rniga); (2) preview-og'ir modullar uchun alohida kichik SwiftPM/Xcode loyihasi saqlash; (3) `rules_xcodeproj`ning yangi relizlarini kuzatib borish — Preview ustida ish ketyapti, lekin hali chiqmagan. Buni loyiha tanlashda hisobga oling (20-dars, Bazel vs Tuist).

> **🧠 Esda tuting — nega shunday:** `rules_xcodeproj` falsafasi (design-goals.md) aniq aytadi: "Xcode xususiyatini yoqish uchun target build qilinishini o'zgartirmaymiz." SwiftUI Preview static kutubxonalar bilan ishlamaydi va `rules_xcodeproj` sizning target'ingiz mahsulot turini o'zgartirib qo'ymaydi. Bu — printsipial tanlov: natija `bazel build` bilan bir xil bo'lishi preview qulayligidan muhimroq deb hisoblanadi.

---

## 7. .bazelrc config oilasi va command-line API {#configlar}

### 7.1. Config oilasi

Generatsiya qilinganda `Kitoblar.xcodeproj/rules_xcodeproj/bazel/xcodeproj.bazelrc` fayli yaratiladi. Unda bir oila config bor — har biri `--config=` orqali chaqiriladi:

| Config                            | Qachon ishlaydi                  | Nima qiladi                                          |
| --------------------------------- | -------------------------------- | ---------------------------------------------------- |
| `rules_xcodeproj`                 | Xcode ichida har build           | Asosiy config; qolganlari undan meros oladi          |
| `rules_xcodeproj_generator`       | `bazel run //:xcodeproj` paytida | Generatsiya (build'ga ta'sir qilmaydigan sozlamalar) |
| `rules_xcodeproj_indexbuild`      | Xcode Index Build (fon)          | Telemetriya/log'ni o'chiradi (tez-tez ishlaydi)      |
| `rules_xcodeproj_swiftuipreviews` | Xcode Preview build              | Preview uchun maxsus copt'lar                        |
| `rules_xcodeproj_coverage`        | Coverage yoqilgan test build     | Coverage ma'lumotini mutlaq yo'llar bilan yozadi     |

Asosiy config'ning bir qismi (bizning generatsiya qilingan fayldan):

```text
common:rules_xcodeproj --verbose_failures
common:rules_xcodeproj --cache_computed_file_digests=500000
common:rules_xcodeproj --compilation_mode=dbg
common:rules_xcodeproj --features=swift.index_while_building
common:rules_xcodeproj --features=swift.use_global_index_store
common:rules_xcodeproj --spawn_strategy=remote,worker,local
build:rules_xcodeproj --remote_download_outputs=toplevel
```

> **💡 Pro tip:** biror build flag'ni Xcode ichidagi build uchun ham, generatsiya uchun ham qo'shmoqchi bo'lsangiz — uni `common:rules_xcodeproj` (asosiy config) ga qo'ying, chunki qolganlari undan meros oladi. Hujjat ogohlantiradi: "build'ga ta'sir qiluvchi flag'lar barcha `rules_xcodeproj{_*}` config'larda bir xil bo'lishi kerak", shuning uchun odatda asosiy config'ni o'zgartirasiz. O'zingizning sozlamalaringizni ildiz `.bazelrc`ingizdagi `common:rules_xcodeproj --...` qatorlariga yozishingiz mumkin.

### 7.2. rules_xcodeproj o'zining alohida output base'ida ishlaydi

Muhim nozik jihat: `rules_xcodeproj` Xcode ichida qurar ekan, u **alohida output base** ishlatadi. Buning sababi — Xcode build'lari sizning oddiy `bazel build`laringizning analysis cache'ini buzmasligi uchun. Buni command-line API bilan tekshirdik:

```bash
bazel run //:xcodeproj -- 'info output_path'
```

```text
INFO: Elapsed time: 1.739s, Critical Path: 0.04s
/Users/.../Caches/bazel/_bazel_codingtech/<hash>/rules_xcodeproj.noindex/build_output_base/execroot/_main/bazel-out
```

E'tibor bering: yo'lda `rules_xcodeproj.noindex/build_output_base` bor — bu sizning oddiy `bazel-out`ingiz emas, **alohida muhit**.

### 7.3. Command-line API

Shu alohida muhitda buyruq berish uchun maxsus API bor:

```bash
bazel run //:xcodeproj -- [variantlar] 'buyruq matni'
```

Foydali misollar:

```bash
# rules_xcodeproj muhitida output_path'ni ko'rsatish (yuqorida)
bazel run //:xcodeproj -- 'info output_path'

# rules_xcodeproj output base'ini tozalash (oddiy "bazel clean" buni tozalamaydi!)
bazel run //:xcodeproj -- clean

# CI'da cache'ni "warm" qilish uchun barcha target'larni qurish
bazel run //:xcodeproj -- --generator_output_groups=all_targets 'build --remote_download_minimal'
```

> **⚠️ Diqqat:** oddiy `bazel clean` `rules_xcodeproj`ning alohida output base'ini **tozalamaydi**. Agar Xcode ichidagi build'da g'alati muammo bo'lsa, `bazel run //:xcodeproj -- clean` ni ishlating (yoki butunlay hammasini tozalaydigan `bazel clean --expunge`). Bu — yangi boshlovchilar ko'p adashadigan joy.

> **💡 Pro tip (CI uchun):** `--generator_output_groups=all_targets` bilan cache warming — [Bazel 11](/ios-lessons/bazel-11-ci-cd) darsining muhim qismi. BuildBuddy'ning migratsiya qo'llanmasi (Brentley Jones, 2024) aytganidek: "`rules_xcodeproj` CLI'dan biroz boshqacha konfiguratsiya bilan quradi, shuning uchun CI cache'ini aynan `rules_xcodeproj` buyrug'i orqali warm qilish kerak." Aks holda dasturchining Xcode'i cache'dan to'liq foyda ko'rmaydi.

---

## 8. Focused projects: katta repo uchun {#focused}

Katta monorepo'da (yuzlab target) hamma target'ni Xcode'ga tiqish — loyihani og'irlashtiradi va indexing'ni sekinlashtiradi. Yechim — **Focused Projects**: Xcode'ga faqat siz hozir ustida ishlayotgan target'larni qo'yish, qolganlarini Bazel binar sifatida qurish.

`xcodeproj` qoidasida ikkita atribut shu uchun:

- `focused_targets` — faqat shu target'lar (va ularga kerakli narsalar) Xcode'ga tushadi; qolganlari "unfocused" bo'ladi.
- `unfocused_targets` — aksincha, faqat shu target'larni Xcode'dan chiqarib tashlaydi.

Masalan, agar siz faqat `App`ustida ishlayotgan bo'lsangiz, lekin `Core`ni Bazel qursin desangiz:

```starlark
# BUILD.bazel
xcodeproj(
    name = "xcodeproj",
    project_name = "Kitoblar",
    tags = ["manual"],
    top_level_targets = [
        top_level_target("//App:Kitoblar", target_environments = ["simulator"]),
        "//Core:CoreTests",
    ],
    # Core'ni Xcode'da ko'rsatmaymiz — u Bazel tomonidan binar sifatida quriladi.
    unfocused_targets = ["//Core:Core"],
)
```

> **🧠 Esda tuting:** "Unfocused" target'lar yo'qolmaydi — ular hamon Bazel tomonidan quriladi va ilovaga ulanadi. Shunchaki Xcode navigator'ida ularning manba fayllarini ko'rmaysiz. Bu Xcode loyihasini yengillashtiradi: kamroq fayl, tezroq indexing, kamroq scheme.

> **💡 Pro tip:** katta jamoalarda `focused_targets`ni **dinamik** qilib qo'yishadi — har dasturchi o'zi ishlayotgan modullarni environment variable yoki alohida `.bzl` fayl orqali ko'rsatadi. `rules_xcodeproj` design-goals.md aynan shuni tavsiya qiladi: "dinamik holatlar uchun `.bzl` fayllarni generatsiya qilib, ulardagi macro'lar orqali kerakli target'larni yarating." Shunda har kim o'ziga kerakli "kichik" loyiha oladi, lekin cache umumiy qoladi.

---

## 9. Katta loyihaga xos muammolar va signing {#muammolar}

`rules_xcodeproj` kuchli, lekin kamchiliksiz emas. Mana halol kamchiliklar ro'yxati (hammasi hujjat yoki bizning tajribamizdan):

### 9.1. Archive ishlamaydi — .ipa'ni Bazel bilan quring

Xcode'ning `Product > Archive > Organizer` oqimi `rules_xcodeproj` loyihalarida **qo'llab-quvvatlanmaydi**. FAQ ochiq aytadi: "Hozircha Archive action'ni to'g'ri ishlashiga kuch sarflanmagan" va optimizatsiyalar hosil bo'ladigan `.xcarchive`ni buzishi mumkin.

Yechim — App Store uchun `.ipa`ni to'g'ridan-to'g'ri `bazel build` bilan quring:

```bash
# Release build (device uchun, arm64)
bazel build //App:Kitoblar -c opt --ios_multi_cpus=arm64
```

Biz simulyator uchun `.ipa`ni allaqachon qurgandik (FACTS tasdiqlaydi):

```text
Target //App:Kitoblar up-to-date:
  bazel-bin/App/Kitoblar.ipa        (149 KB)
```

> **⚠️ Diqqat — device signing:** haqiqiy qurilma/App Store uchun kod imzolash kerak. Bu Apple Developer hisobini talab qiladi, shuning uchun biz uni bu yerda hujjat asosida tushuntiramiz (bizning muhitda device signing tekshirilmagan). [Bazel 8](/ios-lessons/bazel-08-ilova-anatomiyasi)da signing'ni ko'rgandik; releasi va Fastlane esa [Bazel 11](/ios-lessons/bazel-11-ci-cd)da.

### 9.2. Signing: xcode_provisioning_profile

Bazel'da signing Xcode'ning "Automatically manage signing"idan boshqacha ishlaydi. `ios_application`da `provisioning_profile` atributi bo'ladi (odatda `local_provisioning_profile` bilan to'ldiriladi). Agar Xcode boshqaradigan profilni ishlatmoqchi bo'lsangiz, `rules_xcodeproj`ga buni aytish uchun uni `xcode_provisioning_profile` bilan o'raysiz:

```starlark
ios_application(
    name = "Kitoblar",
    # ...
    provisioning_profile = ":xcode_profile",
)

xcode_provisioning_profile(
    name = "xcode_profile",
    managed_by_xcode = True,
    provisioning_profile = ":provisioning_profile",
)

local_provisioning_profile(
    name = "provisioning_profile",
    profile_name = "iOS Team Provisioning Profile: uz.azimjondev.kitoblar",
    team_id = "A12B3CDEFG",
)
```

`managed_by_xcode = True` — Xcode'da "Automatic Code Signing" yoqiladi; `team_id` esa `DEVELOPER_TEAM` build setting'ini belgilaydi. FAQ eslatadi: profil `~/Library/MobileDevice/Provisioning Profiles`ga o'rnatilgan bo'lishi kerak, aks holda Xcode "couldn't find any provisioning profiles matching 'TEAM_ID/PROFILE_NAME'" deydi.

### 9.3. BUILD o'zgarsa — qayta generatsiya kerak

Bu — eng muhim kundalik cheklov. `BUILD.bazel`ni o'zgartirsangiz (yangi fayl, yangi dependency, yangi target), Xcode loyihasi avtomatik yangilanmaydi — `bazel run //:xcodeproj`ni qayta ishga tushirishingiz kerak. Bizning o'lchovimizda bu arzon (8.75 s), lekin **qayta generatsiyadan keyin Xcode'ning DerivedData'si bekor bo'ladi** va keyingi build to'liq bo'ladi (biz buni ko'rdik: o'zgarishsiz incremental 1.99 s edi, lekin qayta generatsiyadan keyin 90.8 s to'liq build).

> **⚠️ Diqqat:** yangi Swift faylini modulning mavjud `Sources/` papkasiga qo'shsangiz va `BUILD.bazel`da `glob(["Sources/**/*.swift"])` ishlatgan bo'lsangiz — target o'zgaradi, lekin Xcode uni ko'rishi uchun baribir qayta generatsiya kerak. (Tuist'ning "buildable folders" xususiyati bu muammoni yumshatadi — [Tuist darslariga](/ios-lessons/tuist-01-kirish-va-ornatish) qarang. Bazel tomonda yangi "synchronized folders" ustida ish ketyapti, lekin 4.1.0 da chiqmagan.)

### 9.4. Stale fayllar va build mode

FAQ yana ikki nozik holatni aytadi:

- **Stale fayllar:** agar dependency o'chirsangiz yoki fayl yo'li o'zgarsa va loyihani yopib-ochmasdan qayta generatsiya qilsangiz, Xcode eski yo'llar haqida ogohlantirishi mumkin (bu Xcode'ning caching bug'i). Yechim — loyihani yopib-ochish yoki clean build.
- **Har modulning manbasi o'z papkasida bo'lishi kerak** (BuildBuddy qo'llanmasi): `glob()` paket chegarasidan o'tmaydi, shuning uchun aralash tuzilishlar muammo beradi.

### 9.5. Kamchiliklar jadvali (halol xulosa)

| Kamchilik                             | Ta'siri                            | Nega                                              |
| ------------------------------------- | ---------------------------------- | ------------------------------------------------- |
| SwiftUI Preview ishlamaydi            | Dizayn-og'ir ish qiyinlashadi      | `ENABLE_DEBUG_DYLIB = NO` (PR #3042); #3201 ochiq |
| Archive yo'q                          | App Store'ga `.ipa`ni Bazel quradi | FAQ: Archive qo'llab-quvvatlanmaydi               |
| BUILD o'zgarsa qayta generatsiya      | Kichik ortiqcha qadam              | loyiha BUILD fayllardan generatsiya qilinadi      |
| Qayta generatsiya → DerivedData bekor | Keyingi build to'liq               | Xcode caching                                     |
| Coverage mutlaq yo'llar yozadi        | Cache hit kamayadi                 | hermetik emas                                     |
| Majorlar tez-tez buziladi             | Yangilashda ehtiyot                | 2.0→3.0→4.0 ~2 yilda                              |
| README jadvali eskirgan               | Xcode 27 "rasman" sinalmagan       | o'zingiz tekshiring                               |

> **🏢 Real-world:** `rules_ios` (muqobil iOS ruleset) ni Square tark etyapti: muhokama [#952](https://github.com/bazel-ios/rules_ios/discussions/952)da (2025-11-07) aytilishicha, ular "faol hissa qo'shishni to'xtatadi", chunki "`rules_ios` kamroq maqsadga xizmat qilyapti va build'ga ortiqcha murakkablik qo'shyapti" — zero rules_apple va rules_swift kerakli funksiyalarni olgan. `rules_xcodeproj` 4.0.0 ham o'zining `rules_ios` misolini olib tashlagan. Xulosa: bugun **rules_apple + rules_swift + rules_xcodeproj** — standart yo'l.

---

## 10. Alternativa: VS Code / Cursor — sourcekit-lsp + BSP {#vscode}

Hamma ham Xcode'ni yoqtirmaydi. Ba'zilar VS Code yoki Cursor'da (yoki AI agent, masalan Claude Code'da) ishlashni afzal ko'radi. Bu ham mumkin — `sourcekit-lsp` (Swift'ning rasmiy Language Server'i) orqali.

Muammo: `sourcekit-lsp` kodingiz qanday qurilishini bilishi kerak (qaysi flag'lar, qaysi modullar). Oddiy SwiftPM loyihada buni o'zi tushunadi, lekin Bazel loyihasida yo'q. Bu bo'shliqni **Build Server Protocol (BSP)** to'ldiradi: BSP server `sourcekit-lsp`ga "bu fayl shunday quriladi" deb aytadi.

Bazel uchun bunday BSP server bor: **[spotify/sourcekit-bazel-bsp](https://github.com/spotify/sourcekit-bazel-bsp)** (Apache-2.0, Spotify ochiq kodi, misol versiya 0.8.3). Zanjir shunday:

```text
Cursor / VSCode / Claude Code
        │
        ▼
   sourcekit-lsp  ──►  sourcekit-bazel-bsp (BSP server)  ──►  Bazel
```

![Uch xil IDE varianti: Xcode + rules_xcodeproj, VS Code/Cursor + sourcekit-bazel-bsp, oddiy Xcode loyihasi + xcode-build-server — har birining ishlaydigan va ishlamaydigan tomoni](/lessons/images/bazel/bazel_09_ide_options.svg)

### 10.1. Nima ishlaydi

`sourcekit-bazel-bsp` README'siga ko'ra:

- **Barcha IDE + Claude:** odatdagi indexing xususiyatlari — code completion, jump to definition (Swift, Obj-C, C++ uchun).
- **Faqat Cursor / VSCode:** build qilish, ishga tushirish, debug (`lldb-dap` orqali), test, hamda simulyator boshqaruvi — IDE ichidan (ixtiyoriy "companion extension" orqali).

### 10.2. Sozlash (qisqa)

`MODULE.bazel`ga dependency qo'shiladi va BUILD'da `setup_sourcekit_bsp` qoidasi e'lon qilinadi:

```starlark
# MODULE.bazel
bazel_dep(name = "sourcekit_bazel_bsp", version = "0.8.3")
```

```starlark
# BUILD.bazel
load("@sourcekit_bazel_bsp//rules:setup_sourcekit_bsp.bzl", "setup_sourcekit_bsp")

setup_sourcekit_bsp(
    name = "setup_sourcekit_bsp",
    files_to_watch = ["App/**/*.swift", "Core/**/*.swift"],
    targets = ["//App:Kitoblar"],
)
```

```bash
# BSP sozlamasini yaratish (qoida argumentlari o'zgarsa qayta ishga tushiring)
bazel run //:setup_sourcekit_bsp
```

Keyin VS Code / Cursor'da Swift extension o'rnatiladi va `.vscode/settings.json`da `sourcekit-lsp` binary yo'li ko'rsatiladi; "Swift: Restart LSP Server" bosiladi. Talablar: Swift 6.1+ toolchain (Xcode 16.4+ ekvivalenti), Xcode baribir o'rnatilgan bo'lishi kerak.

> **⚠️ Diqqat — bu tekshirilmagan:** biz bu bo'limni `sourcekit-bazel-bsp` README'si va Swift forumlari asosida tushuntirdik; o'z muhitimizda sinab ko'rmadik (u alohida o'rnatish va toolchain talab qiladi). Shuning uchun aniq buyruqlar versiyaga qarab o'zgarishi mumkin — repo'ni tekshiring.

### 10.3. Cheklovlar (README + forum)

- **Faqat Bazel loyihalari uchun** — oddiy Xcode loyihalarida ishlamaydi (ular uchun `xcode-build-server`).
- **WMO (Whole Module Optimization) ishlatmang** — u indexing'ni buzadi.
- **Birinchi indexing sekin** — katta loyihada "to'liq indexing uzoq vaqt olishi mumkin". `//...` kabi keng wildcard'lardan qoching.
- **SwiftPM va Swiftly integratsiyalarini o'chiring** — ular BSP bilan to'qnashadi.
- Xcode'ning o'z `sourcekit-lsp`idan foydalaniladi — "Xcode bilan hali kelmagan LSP xususiyatlari" bo'lishi mumkin, ya'ni performans va qulaylikda murosalar.
- Forumda xabar qilingan real muammo: Xcode 26.0.1 ga yangilangandan keyin har Swift faylda "Loading the standard library failed" xatosi (sourcekit-lsp issue #2328); katta iOS loyihada Bazel chaqiruvlari o'rtacha 30 soniya–1 daqiqa, completion shu vaqtda "qotadi".

### 10.4. Oddiy Xcode loyihasi + xcode-build-server (uchinchi variant)

Agar sizda Bazel generatsiya qilgan `.xcodeproj` bor bo'lsa-yu, lekin VS Code'da ishlamoqchi bo'lsangiz, uchinchi yo'l bor: `xcode-build-server` — har qanday Xcode loyihasidan (jumladan Bazel generatsiya qilgani) BSP orqali `sourcekit-lsp`ni ishlata oladi. `sourcekit-bazel-bsp` README'si oddiy Xcode loyihalari uchun aynan shuni tavsiya qiladi.

### 10.5. Taqqoslash jadvali

|                   | Xcode + rules_xcodeproj | VS Code/Cursor + sourcekit-bazel-bsp |
| ----------------- | ----------------------- | ------------------------------------ |
| Muharrir          | Xcode (tanish)          | VS Code / Cursor / Claude            |
| Indexing          | ✅ (Index Build)        | ✅ (Swift/ObjC/C++)                  |
| Build/Run         | ✅ Cmd+R                | ✅ (Cursor/VSCode)                   |
| Debug             | ✅ LLDB                 | ✅ lldb-dap (Cursor/VSCode)          |
| Test navigator    | ✅                      | ✅ (Cursor/VSCode)                   |
| SwiftUI Preview   | ❌ (4.1.0)              | ❌                                   |
| Yetuklik          | Yuqori (19 kompaniya)   | Yangiroq (Spotify, 0.8.x)            |
| Birinchi indexing | Index Build             | sekin (katta loyihada)               |
| AI agent (Claude) | —                       | ✅ indexing                          |

> **🚀 Motivatsiya:** AI agent'lar davrida `sourcekit-bazel-bsp`ning muhim yutug'i — u Claude kabi agent'larga LSP integratsiyasi orqali "kod aqlini" beradi. Ya'ni agent sizning Bazel loyihangizda jump-to-definition va completion'dan foydalanib, kodni to'g'riroq o'zgartiradi. Bu rules_xcodeproj (Xcode-markazli) bera olmaydigan narsa.

> **💡 Pro tip — qaysi birini tanlash:** jamoangiz Xcode'ga o'rgangan bo'lsa va breakpoint/test navigator muhim bo'lsa — **rules_xcodeproj** (sinovdan o'tgan, keng tarqalgan). Dasturchilaringiz VS Code/Cursor/AI-agent'ni afzal ko'rsa va WMO'siz yashay olsangiz — **sourcekit-bazel-bsp**. Ko'pincha jamoalar ikkala yo'lni ham ochiq qoldiradi: bitta Bazel graph, ikki xil muharrir.

---

## Amaliy mashqlar {#mashqlar}

1. **Loyihani generatsiya qiling.** [Bazel 5](/ios-lessons/bazel-05-ios-ilova)dagi "Kitoblar" ilovangizga `rules_xcodeproj` 4.1.0 ni qo'shing, ildiz `BUILD.bazel`ga `xcodeproj` target yozing va `bazel run //:xcodeproj` bilan loyihani yarating. `open Kitoblar.xcodeproj` qilib, ilovani simulyatorda (Cmd+R) ishga tushiring.

2. **Visibility xatosini ataylab keltirib chiqaring.** `//App:Kitoblar` target'idan `visibility = ["@rules_xcodeproj//xcodeproj:generated"]` ni olib tashlang va qayta generatsiya qiling. Xatoni o'qing, keyin visibility'ni qaytaring. Maqsad: xatoni tanish.

3. **Breakpoint va LLDB.** `BookCatalog.search` metodiga breakpoint qo'ying, ilovani ishga tushirib qidiruvda biror so'z yozing. To'xtaganda LLDB konsolida `po q` va `po books.count` ni sinang. Natijani yozib qo'ying.

4. **Command-line API.** `bazel run //:xcodeproj -- 'info output_path'` va `bazel run //:xcodeproj -- clean` ni ishlating. Birinchisi ko'rsatgan yo'lda `rules_xcodeproj.noindex` borligini tasdiqlang. Nega oddiy `bazel-out`dan farq qilishini bir jumlada tushuntiring.

5. ⭐ **Focused project.** `unfocused_targets = ["//Core:Core"]` qo'shib qayta generatsiya qiling. Xcode navigator'ida `Core` modulining manba fayllari yo'qolganini, lekin ilova baribir qurilib ishlashini tekshiring. Qachon bu foydali ekanini yozing.

6. ⭐ **SwiftUI Preview holatini tekshiring.** `grep -o "ENABLE_DEBUG_DYLIB = [A-Z]*" Kitoblar.xcodeproj/project.pbxproj | sort | uniq -c` ni ishlating. Qiymatni toping. Xcode'da `#Preview`ni ochib, canvas'da nima bo'lishini (yoki bo'lmasligni) yozing.

7. ⭐⭐ **O'lchang.** `bazel run //:xcodeproj`ni birinchi marta (bo'sh output base) va `BUILD.bazel`ga bir qator qo'shgandan keyin (qayta) o'lchang (`/usr/bin/time -p` bilan). Keyin `xcodebuild ... build`ni ikki marta ketma-ket qiling va ikkinchisini o'lchang. O'z raqamlaringizni bizning jadvalimiz bilan solishtiring — mashinangiz sekinroq/tezroq bo'lishi mumkin.

---

## O'zingizni tekshiring {#checklist}

- [ ] `rules_xcodeproj` nega kerakligini va Bazel'ni iOS'da qabul qilishdagi #1 to'siq IDE/DX ekanini tushuntira olaman (Asana misoli).
- [ ] `xcodeproj` qoidasidagi `project_name`, `top_level_targets`, `top_level_target(... target_environments=...)` nima qilishini bilaman.
- [ ] Ko'p paketli loyihada `@rules_xcodeproj//xcodeproj:generated` visibility nega kerakligini ayta olaman.
- [ ] "Build with Bazel" (BwB) modelini — `BazelDependencies` target va "Generate Bazel Dependencies" script phase'ni — tushuntira olaman.
- [ ] 4.1.0 da `build_mode` atributi yo'q va BwB — yagona rejim ekanini bilaman.
- [ ] SwiftUI Preview 4.1.0 da nega ishlamasligini (`ENABLE_DEBUG_DYLIB = NO`, #3201) va yechimlarni bilaman.
- [ ] Archive qo'llab-quvvatlanmasligini va `.ipa`ni `bazel build` bilan qurishni bilaman.
- [ ] `bazel run //:xcodeproj -- clean` oddiy `bazel clean`dan nega farq qilishini tushunaman.
- [ ] VS Code/Cursor'ni `sourcekit-bazel-bsp` orqali Bazel'ga ulash mumkinligini va uning cheklovlarini bilaman.

---

## Xulosa {#xulosa}

- **IDE integratsiyasi — Bazel'ni iOS'da qabul qilishdagi asosiy to'siq.** `rules_xcodeproj` aynan shu muammoni hal qiladi: Bazel target'laridan haqiqiy `.xcodeproj` generatsiya qiladi (`bazel run //:xcodeproj`).
- **Model — "Build with Bazel" (BwB):** Xcode muharrir, Bazel esa haqiqiy build'ni bajaradi (`BazelDependencies` target orqali). 4.1.0 da bu — yagona rejim (`build_mode` atributi yo'q).
- **Biz tekshirdik (Xcode 27.1 + rules_xcodeproj 4.1.0):** loyiha generatsiya qilindi (181 s birinchi marta, keyin ~9 s), `xcodebuild` bilan **BUILD SUCCEEDED**, incremental build 1.99 s. README jadvali eskirgan bo'lsa ham, 4.1.0 bu yerda ishladi.
- **Ishlaydi:** indexing, debugging (LLDB), test navigator, inline xatolar. **Ishlamaydi/cheklangan:** SwiftUI Preview (`ENABLE_DEBUG_DYLIB = NO`, #3201 ochiq), Archive (`.ipa`ni Bazel quradi), coverage cache'ni buzadi.
- **Katta repo uchun:** `focused_targets`/`unfocused_targets` bilan Xcode'ni yengillashtiring; CI cache'ni `--generator_output_groups=all_targets` bilan warm qiling.
- **Alternativa:** VS Code/Cursor/Claude — `sourcekit-lsp` + `sourcekit-bazel-bsp` (Spotify) orqali; indexing hamma joyda, build/debug Cursor/VSCode'da; lekin WMO'siz va sekinroq birinchi indexing bilan.
- **Keyingi dars:** [Bazel 10: Cache — action key, disk cache, remote cache, Build without the Bytes va cache-miss detektivligi](/ios-lessons/bazel-10-cache) — Xcode ichidagi build ham, CI ham bir xil cache'ni bo'lishishi sirini ochamiz.

---

## Manbalar {#manbalar}

1. [rules_xcodeproj — GitHub (Mobile Native Foundation)](https://github.com/MobileNativeFoundation/rules_xcodeproj) — README, xususiyatlar ro'yxati, foydalanuvchi loyihalar.
2. [rules_xcodeproj — Usage docs](https://github.com/MobileNativeFoundation/rules_xcodeproj/blob/main/docs/usage.md) — config oilasi, command-line API, output base.
3. [rules_xcodeproj — Bazel rules reference](https://github.com/MobileNativeFoundation/rules_xcodeproj/blob/main/docs/bazel.md) — `xcodeproj`, `top_level_target`, `xcode_provisioning_profile` atributlari.
4. [rules_xcodeproj — FAQ](https://github.com/MobileNativeFoundation/rules_xcodeproj/blob/main/docs/faq.md) — Archive, visibility, stale fayllar, signing.
5. [rules_xcodeproj — Design goals](https://github.com/MobileNativeFoundation/rules_xcodeproj/blob/main/docs/design-goals.md) — BwB/BwX falsafasi, SwiftUI Preview non-goal.
6. [rules_xcodeproj — CHANGELOG](https://github.com/MobileNativeFoundation/rules_xcodeproj/blob/main/CHANGELOG.md) — 4.0.0/4.1.0 o'zgarishlar, legacy generator olib tashlanishi.
7. [Issue #3201 — Xcode SwiftUI Preview Support](https://github.com/MobileNativeFoundation/rules_xcodeproj/issues/3201) — Preview bo'shlig'i (ochiq).
8. [Asana — Scaling a mature iOS codebase with Tuist](https://asana.com/inside-asana/scaling-a-mature-ios-codebase-with-tuist) — nega Bazel'dan voz kechildi (breakpoint/DX), 2023-02-21.
9. [spotify/sourcekit-bazel-bsp](https://github.com/spotify/sourcekit-bazel-bsp) — VS Code/Cursor/Claude + sourcekit-lsp + Bazel (Apache-2.0).
10. [sourcekit-lsp — issue #2328](https://github.com/swiftlang/sourcekit-lsp/issues/2328) — "Loading the standard library failed" (BSP bilan).
11. [rules_ios — discussion #952](https://github.com/bazel-ios/rules_ios/discussions/952) — Square'ning rules_ios'dan chiqishi (2025-11-07).
12. [BuildBuddy — How to migrate an iOS app to Bazel](https://www.buildbuddy.io/blog/how-to-migrate-an-ios-app-to-bazel/) — Brentley Jones, 2024; Archive/cache warming caveat'lari.
