05. Migration — Xcode → Tuist
🚀 Tuist — Mukammal Qo‘llanma & Amaliyot
🔄 Migration — Xcode Loyihasidan Tuistga O'tish
"Eng yaxshi vaqt daraxtni 20 yil oldin ekish edi. Ikkinchi eng yaxshi vaqt — hozir. Loyihangizni Tuist'ga ko'chiring!"
Nima uchun Migration kerak?
Sizda allaqachon ishlayotgan Xcode loyiha bor. Nega uni Tuist'ga o'tkazish kerak?
| Muammo (hozirgi) | Yechim (Tuist bilan) |
|---|---|
.xcodeproj da merge conflict'lar | .xcodeproj Git'da bo'lmaydi |
| Build settings tartibsiz | .xcconfig fayllarda toza saqlash |
| Dependency'lar qo'lda boshqariladi | Tuist avtomatik graph validation |
| Build time uzoq | Binary caching bilan tezlashtirish |
| Yangi dasturchi tushunmaydi | Project.swift o'zi dokumentatsiya |
Migration Strategiyasi
Migration bir kunda qilinmaydi. Bosqichma-bosqich, har bir qadamda tekshirib borish kerak.
📋 Migration Rejasi:
┌─────────────────────────────────────┐
│ 1. Scaffold yaratish │
│ 2. CI da parallel build qo'shish │
│ 3. Build settings → .xcconfig │
│ 4. Package dependencies ko'chirish │
│ 5. Target migration tartibini │
│ aniqlash │
│ 6. Target'larni birma-bir migrate │
│ qilish │
│ 7. Eski .xcodeproj ni o'chirish │
│ 8. App Store Archive tekshirish │
└─────────────────────────────────────┘
1-Qadam: Scaffold Yaratish
Avval Tuist uchun kerakli fayllarni yarating:
# Loyiha papkasiga kiring
cd /path/to/MyApp
Tuist.swift yaratish
// Tuist.swift
import ProjectDescription
let tuist = Tuist()
Project.swift yaratish
// Project.swift
import ProjectDescription
let project = Project(
name: "MyApp-Tuist", // ⚠️ -Tuist suffix — eski project bilan conflict bo'lmasligi uchun
targets: [
// Target'lar keyinroq qo'shiladi
]
)
**Nima uchun
-Tuistsuffix?** Chunki ikkita.xcodeprojbir xil nomda bo'lsa Xcode xato beradi. To'liq migrate qilganingizdan keyin suffix'ni olib tashlaymiz.
Package.swift yaratish
mkdir -p Tuist
// Tuist/Package.swift
// swift-tools-version: 5.9
import PackageDescription
#if TUIST
import ProjectDescription
let packageSettings = PackageSettings(
productTypes: [:]
)
#endif
let package = Package(
name: "MyApp",
dependencies: [
// Dependency'lar keyinroq qo'shiladi
]
)
2-Qadam: CI da Parallel Build
Migration paytida ikkala loyiha (eski Xcode va yangi Tuist) parallel ishlashi kerak. CI pipeline'ga qo'shing:
# CI script — Tuist loyihani ham build qilish
tuist install
tuist generate --no-open
xcodebuild build \
-workspace MyApp-Tuist.xcworkspace \
-scheme MyApp-Tuist \
-destination "platform=iOS Simulator,name=iPhone 16"
Bu har bir PR da Tuist loyiha ham build bo'lishini ta'minlaydi — xato bo'lsa darhol ko'rasiz.
3-Qadam: Build Settings → .xcconfig
Xcode project'dagi build settings'ni .xcconfig fayllariga ko'chiring. Bu migration'ning eng muhim qadami.
Nima uchun?
Build settings .xcodeproj ichida yashiringan. .xcconfig ga ko'chirsangiz:
- Kod sifatida boshqarish mumkin
- Git diff ko'rish oson
- Tuist manifest'da ulash oson
Tuist migration tool
# Papka yaratish
mkdir -p xcconfigs
# Project-level settings ni ko'chirish
tuist migration settings-to-xcconfig \
-p MyApp.xcodeproj \
-x xcconfigs/MyApp-Project.xcconfig
**Natija — xcconfigs/MyApp-Project.xcconfig:**
// Auto-generated by tuist migration
SWIFT_VERSION = 5.9
IPHONEOS_DEPLOYMENT_TARGET = 16.0
CODE_SIGN_STYLE = Automatic
DEVELOPMENT_TEAM = ABCDE12345
// ... boshqa settings
Project.swift ga ulash
let project = Project(
name: "MyApp-Tuist",
settings: .settings(configurations: [
.debug(
name: "Debug",
xcconfig: "./xcconfigs/MyApp-Project.xcconfig"
),
.release(
name: "Release",
xcconfig: "./xcconfigs/MyApp-Project.xcconfig"
),
]),
targets: [...]
)
Settings bo'shligini tekshirish
# Eski project'da settings qolmaganini tekshirish
tuist migration check-empty-settings -p MyApp.xcodeproj
Agar settings qolgan bo'lsa — ularni ham .xcconfig ga ko'chiring.
4-Qadam: Package Dependencies Ko'chirish
Xcode project'dagi SPM dependency'larni Tuist/Package.swift ga ko'chiring.
Xcode da dependency'larni ko'rish
Xcode → Project Navigator → Project → Package Dependencies
Package.swift ga yozish
// Tuist/Package.swift
// swift-tools-version: 5.9
import PackageDescription
#if TUIST
import ProjectDescription
let packageSettings = PackageSettings(
productTypes: [
"Alamofire": .framework,
]
)
#endif
let package = Package(
name: "MyApp",
dependencies: [
.package(url: "https://github.com/Alamofire/Alamofire", from: "5.9.0"),
.package(url: "https://github.com/onevcat/Kingfisher", .upToNextMajor(from: "7.12.0")),
.package(url: "https://github.com/firebase/firebase-ios-sdk", from: "10.0.0"),
]
)
tuist install
5-Qadam: Migration Tartibini Aniqlash
tuist migration list-targets -p MyApp.xcodeproj
Natija (misol):
Target Dependencies
CoreData 0
Networking 1
Analytics 1
HomeFeature 3
ProfileFeature 2
MyApp 5
MyAppTests 6
Qoida: Eng kam dependency bo'lgan target'dan boshlang (yuqoridan pastga). Chunki ular boshqa target'larga bog'liq emas — mustaqil migrate qilish oson.
Migration tartibi:
1. CoreData (0 dependency)
2. Networking (1 dependency)
3. Analytics (1 dependency)
4. ProfileFeature (2 dependencies)
5. HomeFeature (3 dependencies)
6. MyApp (5 dependencies)
7. MyAppTests (6 dependencies)
6-Qadam: Target'larni Birma-bir Migrate Qilish
Har bir target uchun quyidagi qadamlarni takrorlang:
6.1 Target Build Settings → .xcconfig
tuist migration settings-to-xcconfig \
-p MyApp.xcodeproj \
-t CoreData \
-x xcconfigs/CoreData.xcconfig
6.2 Target'ni Project.swift ga qo'shish
// Project.swift
let project = Project(
name: "MyApp-Tuist",
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/MyApp-Project.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/MyApp-Project.xcconfig"),
]),
targets: [
// ✅ Birinchi migrate qilingan target
.target(
name: "CoreData",
destinations: .iOS,
product: .framework,
bundleId: "com.example.CoreData",
sources: ["Sources/CoreData/**"],
dependencies: [],
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/CoreData.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/CoreData.xcconfig"),
])
),
// ✅ Ikkinchi target (CoreData ga bog'liq)
.target(
name: "Networking",
destinations: .iOS,
product: .framework,
bundleId: "com.example.Networking",
sources: ["Sources/Networking/**"],
dependencies: [
.target(name: "CoreData"),
.external(name: "Alamofire"),
],
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/Networking.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/Networking.xcconfig"),
])
),
// ... qolgan target'lar
]
)
6.3 Tekshirish
# Generate
tuist generate --no-open
# Build
xcodebuild build \
-workspace MyApp-Tuist.xcworkspace \
-scheme CoreData \
-destination "platform=iOS Simulator,name=iPhone 16"
# Test (agar test target bor bo'lsa)
tuist test CoreDataTests
6.4 Takrorlash
Har bir target uchun 6.1–6.3 qadamlarni takrorlang. Har bir target uchun alohida Pull Request yarating — code review osonroq bo'ladi.
7-Qadam: Eski .xcodeproj ni O'chirish
Barcha target'lar migrate bo'lganidan keyin:
# 1. Project nomidagi -Tuist suffixni olib tashlash
# Project.swift da: name: "MyApp" (MyApp-Tuist emas)
# 2. Eski fayllarni o'chirish
rm -rf MyApp.xcodeproj
rm -rf MyApp.xcworkspace
# 3. Qayta generate
tuist generate
# 4. .gitignore yangilash
echo "*.xcodeproj" >> .gitignore
echo "*.xcworkspace" >> .gitignore
echo "Derived/" >> .gitignore
8-Qadam: App Store Archive va Publish
Migration'dan keyin eng muhim savol: App Store'ga qanday chiqaraman?
8.1 Tuist bilan Archive Qilish
# 1. Avval generate qiling
tuist install
tuist generate --no-open
# 2. xcodebuild archive
xcodebuild archive \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-destination "generic/platform=iOS" \
-archivePath ./build/MyApp.xcarchive \
CODE_SIGN_IDENTITY="Apple Distribution" \
DEVELOPMENT_TEAM="ABCDE12345"
8.2 Export IPA
# ExportOptions.plist yaratish
cat > ExportOptions.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>ABCDE12345</string>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>
EOF
# IPA export qilish
xcodebuild -exportArchive \
-archivePath ./build/MyApp.xcarchive \
-exportPath ./build/export \
-exportOptionsPlist ExportOptions.plist
8.3 App Store Connect ga Upload
# xcrun altool bilan upload (eski usul)
xcrun altool --upload-app \
-f ./build/export/MyApp.ipa \
-t ios \
-u "your@email.com" \
-p "app-specific-password"
# Yoki yangi usul — Apple Transporter
xcrun notarytool submit ./build/export/MyApp.ipa \
--apple-id "your@email.com" \
--team-id "ABCDE12345" \
--password "app-specific-password"
8.4 Xcode UI orqali Archive (oddiy usul)
Agar terminal bilan qilishni xohlamasangiz:
tuist generate— Xcode ochiladi- Xcode'da: Product → Archive
- Organizer ochiladi → Distribute App
- App Store Connect tanlang → Upload
Muhim: Tuist bilan generate qilingan loyiha oddiy Xcode loyiha bilan bir xil. Archive va publish jarayoni hech qanday farq qilmaydi!
8.5 Signing (Code Imzolash) Sozlash
Project.swift da signing sozlashni unutmang:
.target(
name: "MyApp",
destinations: .iOS,
product: .app,
bundleId: "com.example.MyApp",
// ...
settings: .settings(
base: [
"CODE_SIGN_STYLE": "Automatic",
"DEVELOPMENT_TEAM": "ABCDE12345",
"CODE_SIGN_IDENTITY": "Apple Development",
// Release uchun:
// "CODE_SIGN_IDENTITY": "Apple Distribution",
]
)
)
Migration Checklist ✅
[ ] Tuist.swift yaratildi
[ ] Project.swift scaffold yaratildi
[ ] Tuist/Package.swift yaratildi
[ ] CI da parallel build qo'shildi
[ ] Project build settings → .xcconfig
[ ] Package dependencies → Tuist/Package.swift
[ ] tuist migration list-targets bilan tartib aniqlandi
[ ] Har bir target migrate qilindi:
[ ] Build settings → .xcconfig
[ ] Target → Project.swift
[ ] Build tekshirildi
[ ] Test'lar o'tdi
[ ] Eski .xcodeproj o'chirildi
[ ] .gitignore yangilandi
[ ] App Store Archive ishlaydi
[ ] CI/CD pipeline yangilandi
Troubleshooting
"Compilation errors due to missing files"
❌ Error: MyFile.swift: No such file or directory
Sabab: sources glob pattern noto'g'ri. Yechim: Fayl yo'llarini tekshiring:
sources: ["Sources/MyModule/**"] // ← papka nomini tekshiring
"Build settings don't match"
Sabab: .xcconfig da settings yetishmayapti. Yechim: Eski Xcode project build settings bilan .xcconfig ni solishtiring.
"Framework search paths"
settings: .settings(
base: [
"FRAMEWORK_SEARCH_PATHS": "$(inherited) $(SRCROOT)/Frameworks",
]
)
Xulosa
| Qadam | Buyruq |
|---|---|
| Settings ko'chirish | tuist migration settings-to-xcconfig |
| Target tartibini ko'rish | tuist migration list-targets |
| Settings tekshirish | tuist migration check-empty-settings |
| Generate | tuist generate |
| Archive | xcodebuild archive |
| Test | tuist test |
💡 Keyingi darslik: TMA — Modular Architecture — Loyihani professional tarzda modullarga bo'lish.
"Migration — bu investitsiya. Bugun bir kun sarflaysiz, ertaga har kuni soatlab vaqt tejaysiz!" ⏰