# 🔄 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:

```bash
# Loyiha papkasiga kiring
cd /path/to/MyApp
```

### Tuist.swift yaratish

```swift
// Tuist.swift
import ProjectDescription

let tuist = Tuist()
```

### Project.swift yaratish

```swift
// 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 `-Tuist` suffix?** Chunki ikkita `.xcodeproj` bir xil nomda bo'lsa Xcode xato beradi. To'liq migrate qilganingizdan keyin suffix'ni olib tashlaymiz.

### Package.swift yaratish

```bash
mkdir -p Tuist
```

```swift
// 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:

```bash
# 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

```bash
# 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

```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: [...]
)
```

### Settings bo'shligini tekshirish

```bash
# 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

```swift
// 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"),
    ]
)
```

```bash
tuist install
```

---

## 5-Qadam: Migration Tartibini Aniqlash

```bash
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

```bash
tuist migration settings-to-xcconfig \
    -p MyApp.xcodeproj \
    -t CoreData \
    -x xcconfigs/CoreData.xcconfig
```

### 6.2 Target'ni Project.swift ga qo'shish

```swift
// 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

```bash
# 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:

```bash
# 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

```bash
# 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

```bash
# 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

```bash
# 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:

1. `tuist generate` — Xcode ochiladi
2. Xcode'da: **Product → Archive**
3. Organizer ochiladi → **Distribute App**
4. **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:

```swift
.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:
```swift
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"

```swift
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](06_modular_architecture.md) — Loyihani professional tarzda modullarga bo'lish.

> *"Migration — bu investitsiya. Bugun bir kun sarflaysiz, ertaga har kuni soatlab vaqt tejaysiz!"* ⏰
