02. Manifest Fayllar
🚀 Tuist — Mukammal Qo‘llanma & Amaliyot
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.swiftvaTuist/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.swiftichidagi har bir maydonni (name,options,settings,targets,schemes, ...) tushunasiz va nima uchun kerakligini bilasiz.Targetning 26 ta parametri, ayniqsadestinationsvsdeploymentTargets,productturlari vabuildableFoldersvssources/resourcesfarqini amalda ishlatasiz.Tuist.swiftning 2026-yilgi yangiTuist(project: .tuist(...))shaklini yozasiz va eskirgan (deprecated) API'larni taniysiz.Workspace.swiftbilan ko'p loyihali workspace quryapsiz,Tuist/Package.swiftbilan tashqi paketlarni ulash sintaksisini bilasiz.tuist edit,tuist dumpva manifest caching qanday ishlashini, implicit dependency'nituist inspect dependenciesbilan qanday ushlashni ko'rasiz.
Mundarija
- Manifest nima va nega Swift tilida?
- ProjectDescription va to'rtta manifest fayl
- Project.swift — loyihaning yuragi
- Target anatomiyasi — bitta build mahsuloti
- buildableFolders vs sources/resources globs (2026 yangiligi)
- Settings, configuration va xcconfig
- Scheme'lar — avtomatik va custom
- Tuist.swift — 2026 global konfiguratsiya
- Workspace.swift — ko'p loyihali workspace
- Tuist/Package.swift va PackageSettings
- tuist edit, tuist dump va manifest caching
- To'liq misol: noldan generate → build → test
- Amaliy mashqlar
- O'zingizni tekshiring
- Xulosa
- 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).
.xcodeprojesa — kompilyatsiya mahsuloti kabi: generate qilinadigan, Git'ga qo'shilmaydigan natija. Shuning uchun.xcodeproj/.xcworkspaceodatda.gitignorega 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:
.pbxprojmerge 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 paketlarini (dependency) e'lon qiladi | Yo'q (faqat tashqi paket bo'lsa) |
Tuist manifest xaritasi: qaysi fayl nimani ta'riflaydi va qayerda joylashadi
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):
// 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 (nilyoki default). Eng minimal valid project atiginamevatargetsbilan ishlaydi. Qolgan maydonlarni kerak bo'lgandagina qo'shing (YAGNI) — manifestni behuda shishirmang.
Project.Options ham bir qancha foydali sozlamani ochadi (manbadan, ProjectOptions.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:
// 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
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...). BuSet<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:
// ProjectDescription — Destinations.iOS
public static let iOS: Destinations = [.iPhone, .iPad, .macWithiPadDesign]
Buni men tuist dump chiqishida ham ko'rdim — .iOS aynan shu uchtaga yoyilgan:
"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):
// 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:
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.
🧠 Esda tuting:
tuist dumpchiqishida bog'liqlik aniq ko'rinadi — men yuqoridagi loyihadaBooksAppnituist dumpqilganimda, 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:
.target(
name: "BooksApp",
// ...
sources: ["BooksApp/Sources/**"],
resources: ["BooksApp/Resources/**"]
)
** — rekursiv (barcha ichki papkalar). Ba'zi fayllarni chiqarib tashlash ham mumkin (manbadagi SourceFilesList.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."
.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:
buildableFoldersbilan "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 uchunbuildableFolderstavsiya etiladi.
tuist dump natijasi buildableFolders ni qanday ko'rishini tasdiqlaydi (men o'z loyihamda tekshirdim):
"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:
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/resourcesglobs'da qoling. Xcode 27.1 dabuildableFoldersmen 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:
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:
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:
// 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:
"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):
.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_FAMILYva h.k.)..none— Tuist hech qanday build setting qo'shmaydi (hammasini o'zingiz berasiz).
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
.noneqilib 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:
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 generateni qayta yurgizadigan maxsus scheme. U default bo'yicha qo'shiladi (buniTuist.swiftdagiincludeGenerateSchemeboshqaradi, 8-bo'limga qarang).
🧠 Esda tuting: Test target alohida scheme bo'lmaydi — u o'zi tegishli target scheme'iga biriktiriladi. Men
BooksKitTestsni yaratdim, lekin ro'yxatdaBooksKitTestsdegan scheme yo'q: uning testlariBooksKit(va men yozganKutubxona) scheme'ining test action'ida ishlaydi. Shu sababtuist test BooksKitTestsxato beradi — scheme nomini (BooksKityokiKutubxona) 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):
// 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: falseberib, 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'ningxcshareddatapapkasiga tushadi, ya'nixcodebuilduni command-line'dan ko'radi. CI'da ishlatiladigan har qanday schemeshared: truebo'lishi shart, aks holda CI "scheme topilmadi" deydi.
⚠️ Diqqat (API o'zgarishi):
runActionda ilgariexecutable: "AppName"(string →TargetReference) ishlatilardi. 4.211.1 da bu shakldeprecated; yangi shaklexecutable: .executable("AppName")yoki umuman bermaslik (.runAction(configuration: .debug)). Oddiy, bitta app target'li scheme'daexecutable'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)
// 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'nicompatibleXcodeVersions,plugins,generationOptions,installOptionsendi.tuist(...)ning ichiga ko'chgan.
8.2 Kengaytirilgan Tuist.swift
Konfiguratsiya qo'shmoqchi bo'lsangiz, hammasi .tuist(...) ichiga tushadi (manbadagi TuistProject.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:
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:
let tuist = Tuist(
fullHandle: "mening-tashkilotim/kutubxona",
project: .tuist()
)
⚠️ Diqqat:
fullHandlefaqat 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):
// 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:
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:
// 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:
// 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):
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.swiftda faqat asosiy project'lar yo'lini berasiz — ularning bog'liqliklari (dependency graph) avtomatik qo'shiladi. MenWorkspace.swiftda 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.xcodeprojo'rnigaProjectDescriptionHelpersdan foydalaniladi..xcodeprojGit'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.
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):
// 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; oddiyswift buildbuni ko'rmaydi.PackageSettings— paketlar qanday product turiga aylanishini boshqaradi. **Paketlar default bo'yicha.staticFramework** ga aylanadi (baseProductType), lekinproductTypesorqali ayrimini.frameworkqilish 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/Dependenciesemas** (bu Tuist 3 ning eski yo'li edi).Tuist/.buildni.gitignorega 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 uningTuist/Package.swiftdagidependenciesro'yxati bo'sh. Paketlar, XCFramework, binary cache va.externalning hamma turlari — keyingi dars (T04) mavzusi: 04. Dependency'lar.
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
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:
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):
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
.xcodeprojdan 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 editmanifestlarni**/{Manifest}.swiftglob'i bilan topadi (loyiha ildizidan, ya'niTuist.swiftturgan joydan). Shuning uchun ildizda to'g'riTuist.swiftbo'lishi shart.
11.2 tuist dump — manifest nimaga aylanganini ko'rish
tuist dump manifestingizni Tuist ichki modeliga aylantirib, JSON ko'rinishida chiqaradi. Bu — manifestingiz haqiqatan nima bo'lishini ko'rishning eng aniq usuli (debug uchun oltin):
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 dumpqiling. Masalan, "destinationsto'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-openyugurishlarim (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:
tuist inspect dependencies --only implicit
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:
✖ 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
importqilgan, 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
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
// Tuist.swift
import ProjectDescription
let tuist = Tuist(project: .tuist())
// 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)
),
]
)
// 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
// 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"),
]
}
// BooksApp/Sources/BooksApp.swift
import BooksKit
import SwiftUI
@main
struct BooksApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
// 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")
}
}
}
// 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
# 1. Generate
tuist generate --no-open
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.
# 2. Build (ishlab chiqarilgan workspace'ni xcodebuild bilan)
xcodebuild -workspace Kutubxona.xcworkspace -scheme BooksApp \
-destination 'generic/platform=iOS Simulator' build
** BUILD SUCCEEDED **
# 3. Test (tuist test — custom "Kutubxona" scheme'ini)
tuist test Kutubxona --device "iPhone 17" --os 27.0
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
.xcodeprojfaylga qo'l tegizmadik, bitta ham UUID yozmadik. Butun loyiha — o'qiladigan, review bo'ladigan Swift kodi. Yangi dasturchiProject.swiftni 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:
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:
// 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}
- Minimal loyiha. Bo'sh papkada faqat
Tuist.swift(let tuist = Tuist(project: .tuist())) va bitta target'liProject.swiftyozing.tuist generate --no-openqiling..xcworkspaceochilganini tekshiring. - globs → buildableFolders.
sources: ["App/Sources/**"]bilan loyiha yozing, generate qiling. Keyin unibuildableFolders: ["App/Sources"]ga o'zgartiring va qayta generate qiling.tuist dump projectchiqishida farqni ko'ring ("sources"vs"buildableFolders"). - Ikki configuration + xcconfig. Loyihaga
DebugvaReleaseconfiguration'lar qo'shing, har biriga.xcconfigfayl bog'lang.tuist dump projectda"xcconfig"yo'llari to'g'ri ko'rinayotganini tasdiqlang. - Custom scheme.
schemes:ga o'z scheme'ingizni yozing (build + test action bilan),shared: trueqiling.xcodebuild -workspace <nom>.xcworkspace -listbilan scheme ro'yxatida paydo bo'lganini tekshiring. - Implicit dependency'ni ushlash ⭐. Ikki modulli loyiha yozing; bir modulda ikkinchisini
importqiling, lekindependenciesga qo'shmang.tuist inspect dependencies --only implicityurgizing — xato chiqishini ko'ring. Keyin bog'liqlikni qo'shib, buyruq toza o'tganini tasdiqlang. - Workspace ⭐. Ikkita project'ni (
AppvaFeature) bittaWorkspace.swiftga 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 ProjectDescriptionhar bir manifestning birinchi qatori ekanini va nega kerakligini tushunaman. - [ ]
Project.swiftildizida faqat bittalet project = Project(...)bo'lishi kerakligini bilaman. - [ ]
destinations(qurilma oilalari) vadeploymentTargets(minimal OS versiyasi) farqini ayta olaman. - [ ]
buildableFoldersvasources/resourcesglobs 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.swiftning 2026 shakliTuist(project: .tuist(...))ekanini va eski init deprecated ekanini bilaman. - [ ]
enforceExplicitDependencieso'rnigatuist inspect dependencies --only implicitishlatilishini bilaman. - [ ] Tashqi paketlar
Tuist/Package.swiftda e'lon qilinib,Tuist/.buildga yechilishini bilaman. - [ ]
tuist edit,tuist dumpva manifest caching nima qilishini tushunaman.
Xulosa {#xulosa}
- Tuist loyihasi Swift manifestlar bilan ta'riflanadi;
.xcodeprojesa 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). Hammasiimport ProjectDescriptionbilan boshlanadi. Target— bitta build mahsuloti; eng muhim parametrlardestinations,deploymentTargets,product,bundleId,buildableFolders/sources,dependencies.- 2026 yangiligi: **
buildableFolders** (Xcode 16 synchronized groups) fayl qo'shilganda qayta generate ehtiyojini yo'qotadi. - **
Tuist.swiftning to'g'ri 2026 shakli**Tuist(project: .tuist(...)); eskiTuist(generationOptions:...)vaenforceExplicitDependencies— deprecated. Implicit dependency endituist inspect dependencies --only implicitbilan ushlanadi. tuist dumpmanifestingiz 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
Manbalar {#manbalar}
- Tuist — Manifests (Project.swift, Workspace.swift, Tuist.swift)
- [Tuist — Editing (
tuist edit)](https://tuist.dev/en/docs/guides/features/projects/editing) - [Tuist — Dependencies (
Tuist/Package.swift, PackageSettings)](https://tuist.dev/en/docs/guides/features/projects/dependencies) - Tuist — Code sharing (ProjectDescriptionHelpers)
- [Tuist — Inspect implicit imports (
tuist inspect dependencies)](https://tuist.dev/en/docs/guides/features/projects/inspect/implicit-dependencies) - Tuist changelog — Buildable folders (2025-08-11, #7984)
- ProjectDescription manbasi — Target.swift (manifest API)
- Tuist — v3 dan v4 ga migratsiya (Config.swift → Tuist.swift, tuist fetch → install)
- ProjectDescription reference (Project, Settings, Scheme, BuildableFolder...)
- Asana Engineering — Scaling a mature iOS codebase with Tuist