13. Migration Case Study
🚀 Tuist — Mukammal Qo‘llanma & Amaliyot
🔄 Real Loyiha #5: Migration Case Study
"Eng yaxshi migration — bosqichma-bosqich, har bir qadamda tekshirib borilgan migration!"
Vaziyat
Sizda mavjud Xcode loyiha bor — "NotesApp". U 3 ta target'dan iborat:
NotesApp.xcodeproj
├── NotesApp (iOS App)
├── NotesKit (Framework — biznes logika)
└── NotesAppTests (Unit Tests)
Dependency'lar (SPM orqali):
├── Realm (local database)
└── SnapKit (Auto Layout)
Muammolar:
.xcodeprojda doimiy merge conflict'lar- Build settings tartibsiz
- Yangi dasturchi 1 soat kutadi build uchun
Maqsad: Bu loyihani to'liq Tuist'ga ko'chirish.
1-Qadam: Loyiha Tahlili
Mavjud tuzilma
NotesApp/
├── NotesApp.xcodeproj ← Bu yo'qoladi!
├── NotesApp/
│ ├── AppDelegate.swift
│ ├── SceneDelegate.swift
│ ├── Views/
│ │ ├── NoteListView.swift
│ │ └── NoteDetailView.swift
│ ├── Info.plist
│ └── Assets.xcassets
├── NotesKit/
│ ├── Models/
│ │ └── Note.swift
│ ├── Services/
│ │ └── NoteService.swift
│ └── Info.plist
└── NotesAppTests/
└── NoteServiceTests.swift
Migration tartibi (tuist migration list-targets)
tuist migration list-targets -p NotesApp.xcodeproj
Target Dependencies
NotesKit 0 ← Birinchi shu!
NotesApp 1
NotesAppTests 2 ← Oxirgi shu!
2-Qadam: Tuist Scaffold Yaratish
cd NotesApp/
# Tuist fayllar yaratish
touch Tuist.swift
mkdir -p Tuist
Tuist.swift
import ProjectDescription
let tuist = Tuist()
Project.swift (bo'sh scaffold)
import ProjectDescription
let project = Project(
name: "NotesApp-Tuist", // ⚠️ -Tuist suffix
targets: [
// Target'lar keyinroq qo'shiladi
]
)
Tuist/Package.swift
// swift-tools-version: 5.9
import PackageDescription
#if TUIST
import ProjectDescription
let packageSettings = PackageSettings(
productTypes: [:]
)
#endif
let package = Package(
name: "NotesApp",
dependencies: [
// Hozircha bo'sh — keyinroq qo'shamiz
]
)
3-Qadam: Build Settings → .xcconfig
# Papka yaratish
mkdir -p xcconfigs
# Project-level settings
tuist migration settings-to-xcconfig \
-p NotesApp.xcodeproj \
-x xcconfigs/Project.xcconfig
# Target-level settings
tuist migration settings-to-xcconfig \
-p NotesApp.xcodeproj \
-t NotesKit \
-x xcconfigs/NotesKit.xcconfig
tuist migration settings-to-xcconfig \
-p NotesApp.xcodeproj \
-t NotesApp \
-x xcconfigs/NotesApp.xcconfig
tuist migration settings-to-xcconfig \
-p NotesApp.xcodeproj \
-t NotesAppTests \
-x xcconfigs/NotesAppTests.xcconfig
Tekshirish:
tuist migration check-empty-settings -p NotesApp.xcodeproj
# ✅ No build settings found — barcha settings .xcconfig ga ko'chdi
**xcconfigs/NotesApp.xcconfig misol:**
// Auto-generated
PRODUCT_BUNDLE_IDENTIFIER = com.example.NotesApp
INFOPLIST_FILE = NotesApp/Info.plist
SWIFT_VERSION = 5.9
IPHONEOS_DEPLOYMENT_TARGET = 16.0
CODE_SIGN_STYLE = Automatic
DEVELOPMENT_TEAM = ABCDE12345
TARGETED_DEVICE_FAMILY = 1,2
4-Qadam: SPM Dependencies Ko'chirish
Xcode project'dagi SPM dependency'larni Tuist/Package.swift ga ko'chiring:
// Tuist/Package.swift
// swift-tools-version: 5.9
import PackageDescription
#if TUIST
import ProjectDescription
let packageSettings = PackageSettings(
productTypes: [
"RealmSwift": .framework,
]
)
#endif
let package = Package(
name: "NotesApp",
dependencies: [
.package(url: "https://github.com/realm/realm-swift", from: "10.45.0"),
.package(url: "https://github.com/SnapKit/SnapKit", from: "5.7.0"),
]
)
# Dependency'larni yuklash
tuist install
5-Qadam: NotesKit Target Migrate Qilish (Birinchi!)
Project.swift yangilash
import ProjectDescription
let project = Project(
name: "NotesApp-Tuist",
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/Project.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/Project.xcconfig"),
]),
targets: [
// ✅ NotesKit — birinchi migrate qilingan target
.target(
name: "NotesKit",
destinations: .iOS,
product: .framework,
bundleId: "com.example.NotesKit",
sources: ["NotesKit/**/*.swift"], // Mavjud papka strukturasi
dependencies: [
.external(name: "RealmSwift"),
],
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/NotesKit.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/NotesKit.xcconfig"),
])
),
]
)
Tekshirish
tuist generate --no-open
xcodebuild build \
-workspace NotesApp-Tuist.xcworkspace \
-scheme NotesKit \
-destination "platform=iOS Simulator,name=iPhone 16"
✅ BUILD SUCCEEDED — NotesKit migrate bo'ldi!
6-Qadam: NotesApp Target Migrate Qilish
Project.swift ga qo'shish
targets: [
// NotesKit (avvalgi qadam)
.target(
name: "NotesKit",
// ... (avvalgi kod)
),
// ✅ NotesApp — ikkinchi migrate
.target(
name: "NotesApp",
destinations: .iOS,
product: .app,
bundleId: "com.example.NotesApp",
infoPlist: .file(path: "NotesApp/Info.plist"), // Mavjud Info.plist
sources: ["NotesApp/**/*.swift"],
resources: [
"NotesApp/Assets.xcassets",
"NotesApp/LaunchScreen.storyboard",
],
dependencies: [
.target(name: "NotesKit"),
.external(name: "SnapKit"),
],
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/NotesApp.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/NotesApp.xcconfig"),
])
),
]
Tekshirish
tuist generate --no-open
xcodebuild build \
-workspace NotesApp-Tuist.xcworkspace \
-scheme NotesApp \
-destination "platform=iOS Simulator,name=iPhone 16"
7-Qadam: NotesAppTests Migrate Qilish
// Project.swift targets ga qo'shing:
.target(
name: "NotesAppTests",
destinations: .iOS,
product: .unitTests,
bundleId: "com.example.NotesAppTests",
sources: ["NotesAppTests/**/*.swift"],
dependencies: [
.target(name: "NotesKit"),
.target(name: "NotesApp"),
],
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/NotesAppTests.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/NotesAppTests.xcconfig"),
])
),
Test ishga tushirish
tuist test
Test Suite 'All tests' started
Test Suite 'NoteServiceTests' started
testCreateNote ✅ (0.012s)
testDeleteNote ✅ (0.008s)
testFetchNotes ✅ (0.015s)
Test Suite 'NoteServiceTests' passed (3/3)
✅ ALL TESTS PASSED
8-Qadam: Eski .xcodeproj O'chirish
# Project nomidan -Tuist suffixni olib tashlash
# Project.swift: name: "NotesApp" (NotesApp-Tuist emas)
# Eski fayllarni o'chirish
rm -rf NotesApp.xcodeproj
rm -rf NotesApp.xcworkspace
# .gitignore yangilash
cat >> .gitignore << 'EOF'
# Tuist generated
*.xcodeproj
*.xcworkspace
Derived/
Tuist/.build
EOF
# Qayta generate
tuist generate
9-Qadam: App Store Archive Tekshirish
# Archive
tuist generate --no-open
xcodebuild archive \
-workspace NotesApp.xcworkspace \
-scheme NotesApp \
-configuration Release \
-destination "generic/platform=iOS" \
-archivePath ./build/NotesApp.xcarchive
# ✅ ARCHIVE SUCCEEDED
Yakuniy Project.swift
import ProjectDescription
let project = Project(
name: "NotesApp",
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/Project.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/Project.xcconfig"),
]),
targets: [
.target(
name: "NotesKit",
destinations: .iOS,
product: .framework,
bundleId: "com.example.NotesKit",
sources: ["NotesKit/**/*.swift"],
dependencies: [
.external(name: "RealmSwift"),
],
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/NotesKit.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/NotesKit.xcconfig"),
])
),
.target(
name: "NotesApp",
destinations: .iOS,
product: .app,
bundleId: "com.example.NotesApp",
infoPlist: .file(path: "NotesApp/Info.plist"),
sources: ["NotesApp/**/*.swift"],
resources: [
"NotesApp/Assets.xcassets",
],
dependencies: [
.target(name: "NotesKit"),
.external(name: "SnapKit"),
],
settings: .settings(configurations: [
.debug(name: "Debug", xcconfig: "./xcconfigs/NotesApp.xcconfig"),
.release(name: "Release", xcconfig: "./xcconfigs/NotesApp.xcconfig"),
])
),
.target(
name: "NotesAppTests",
destinations: .iOS,
product: .unitTests,
bundleId: "com.example.NotesAppTests",
sources: ["NotesAppTests/**/*.swift"],
dependencies: [
.target(name: "NotesKit"),
.target(name: "NotesApp"),
]
),
]
)
Migration Checklist ✅
[x] Tuist.swift yaratildi
[x] Project.swift scaffold yaratildi
[x] Tuist/Package.swift yaratildi
[x] Project build settings → xcconfigs/Project.xcconfig
[x] NotesKit build settings → xcconfigs/NotesKit.xcconfig
[x] NotesApp build settings → xcconfigs/NotesApp.xcconfig
[x] SPM dependencies → Tuist/Package.swift
[x] tuist install — dependencies yuklandi
[x] NotesKit target → Project.swift ✅ Build OK
[x] NotesApp target → Project.swift ✅ Build OK
[x] NotesAppTests target → Project.swift ✅ Tests OK
[x] Eski .xcodeproj o'chirildi
[x] .gitignore yangilandi
[x] Archive tekshirildi ✅
Nimani o'rgandik?
| Qadam | Tushuntirish |
|---|---|
tuist migration settings-to-xcconfig | Build settings → .xcconfig |
tuist migration list-targets | Migration tartibi |
tuist migration check-empty-settings | Settings ko'chganini tekshirish |
| Bosqichma-bosqich | Target by target migrate |
| Tekshirish | Har qadamda build va test |
💡 Keyingi darslik: Best Practices va Expert Tips — Ekspert darajasidagi maslahatlar.
"Migration — investitsiya. Bugun sarflagan vaqtingiz, ertaga o'n baravar qaytadi!" 💰