# 🔧 Code Sharing, Templates va App Store Publish

> *"Kodni takrorlamang — ulashing! DRY (Don't Repeat Yourself) prinsipi Tuist manifest'lari uchun ham amal qiladi."*

---

## 1. ProjectDescriptionHelpers — Manifest'lar Orasida Kod Ulashish

`ProjectDescriptionHelpers` — bu Tuist'ning eng kuchli feature'laridan biri. U barcha manifest fayllarda ishlatilishi mumkin bo'lgan **shared Swift code** papkasi.

### Joylashuv

```
MyApp/
├── Tuist/
│   └── ProjectDescriptionHelpers/    ← SHU PAPKA
│       ├── Project+Templates.swift
│       ├── Target+Templates.swift
│       └── Constants.swift
├── Project.swift                      ← import ProjectDescriptionHelpers
└── Tuist.swift
```

### Misol 1: Target Template

```swift
// Tuist/ProjectDescriptionHelpers/Target+Templates.swift
import ProjectDescription

extension Target {
    /// iOS Framework target yaratish uchun helper
    public static func makeFramework(
        name: String,
        sources: String? = nil,
        dependencies: [TargetDependency] = [],
        resources: ResourceFileElements? = nil
    ) -> Target {
        return .target(
            name: name,
            destinations: .iOS,
            product: .framework,
            bundleId: "com.example.\(name)",
            deploymentTargets: .iOS("16.0"),
            infoPlist: .default,
            sources: ["\(sources ?? name)/Sources/**"],
            resources: resources,
            dependencies: dependencies
        )
    }

    /// iOS App target yaratish uchun helper
    public static func makeApp(
        name: String,
        dependencies: [TargetDependency] = []
    ) -> Target {
        return .target(
            name: name,
            destinations: .iOS,
            product: .app,
            bundleId: "com.example.\(name)",
            deploymentTargets: .iOS("16.0"),
            infoPlist: .extendingDefault(with: [
                "UILaunchScreen": [
                    "UIColorName": "",
                    "UIImageName": "",
                ],
            ]),
            sources: ["\(name)/Sources/**"],
            resources: ["\(name)/Resources/**"],
            dependencies: dependencies
        )
    }
}
```

### Misol 2: Konstantalar

```swift
// Tuist/ProjectDescriptionHelpers/Constants.swift
import ProjectDescription

public enum AppConstants {
    public static let organizationName = "com.example"
    public static let deploymentTarget: DeploymentTargets = .iOS("16.0")
    public static let destinations: Destinations = .iOS

    // Build settings
    public static let baseSettings: SettingsDictionary = [
        "SWIFT_VERSION": "5.9",
        "CODE_SIGN_STYLE": "Automatic",
        "DEVELOPMENT_TEAM": "ABCDE12345",
    ]
}
```

### Manifest'da Ishlatish

```swift
// Project.swift
import ProjectDescription
import ProjectDescriptionHelpers  // ← Import!

let project = Project(
    name: "MyApp",
    targets: [
        .makeFramework(
            name: "CoreModule",
            dependencies: [
                .external(name: "Alamofire"),
            ]
        ),
        .makeFramework(
            name: "HomeFeature",
            dependencies: [
                .target(name: "CoreModule"),
            ]
        ),
        .makeApp(
            name: "MyApp",
            dependencies: [
                .target(name: "HomeFeature"),
            ]
        ),
    ]
)
```

> **50 qatorlik** har bir target **5 qatorga** tushdi! Va barcha target'lar **bir xil standart** da yaratiladi.

---

## 2. Templates va `tuist scaffold`

Template'lar yangi module yaratishni **avtomatlashtiradi**.

### Template Yaratish

```bash
mkdir -p Tuist/Templates/Feature
```

```swift
// Tuist/Templates/Feature/Feature.swift
import ProjectDescription

let nameAttribute: Template.Attribute = .required("name")

let template = Template(
    description: "Yangi feature module yaratish",
    attributes: [
        nameAttribute,
    ],
    items: [
        // Sources papka va fayl
        .file(
            path: "Modules/\(nameAttribute)/Sources/\(nameAttribute)View.swift",
            templatePath: "FeatureView.stencil"
        ),
        .file(
            path: "Modules/\(nameAttribute)/Sources/\(nameAttribute)ViewModel.swift",
            templatePath: "FeatureViewModel.stencil"
        ),
        // Tests
        .file(
            path: "Modules/\(nameAttribute)/Tests/\(nameAttribute)ViewModelTests.swift",
            templatePath: "FeatureTests.stencil"
        ),
        // Interface
        .file(
            path: "Modules/\(nameAttribute)/Interface/\(nameAttribute)ServiceProtocol.swift",
            templatePath: "FeatureInterface.stencil"
        ),
    ]
)
```

### Stencil Template Fayllari

```swift
// Tuist/Templates/Feature/FeatureView.stencil
import SwiftUI
import {{ name }}Interface

public struct {{ name }}View: View {
    @StateObject private var viewModel: {{ name }}ViewModel

    public init(viewModel: {{ name }}ViewModel) {
        _viewModel = StateObject(wrappedValue: viewModel)
    }

    public var body: some View {
        Text("{{ name }} View")
    }
}
```

```swift
// Tuist/Templates/Feature/FeatureViewModel.stencil
import Foundation
import {{ name }}Interface

public class {{ name }}ViewModel: ObservableObject {
    private let service: {{ name }}ServiceProtocol

    public init(service: {{ name }}ServiceProtocol) {
        self.service = service
    }
}
```

### Scaffold Buyrug'i

```bash
# Yangi feature yaratish
tuist scaffold Feature --name Settings

# Natija:
# Modules/Settings/Sources/SettingsView.swift
# Modules/Settings/Sources/SettingsViewModel.swift
# Modules/Settings/Tests/SettingsViewModelTests.swift
# Modules/Settings/Interface/SettingsServiceProtocol.swift
```

> **Foyda:** Yangi modul qo'shish **30 soniya** oladi. Va barcha modullar **bir xil strutura** da yaratiladi!

---

## 3. Plugins — Tashqi Tuist Plugin'lar

Plugin'lar — bu bir nechta loyiha orasida Tuist konfiguratsiyasini ulashish uchun ishlatiladi.

### Plugin Yaratish

```
TuistPluginMyCompany/
├── Plugin.swift
├── ProjectDescriptionHelpers/
│   └── CompanyTargetTemplates.swift
└── Templates/
    └── ... (ixtiyoriy)
```

```swift
// Plugin.swift
import ProjectDescription

let plugin = Plugin(name: "MyCompanyPlugin")
```

### Plugin'ni Ishlatish

```swift
// Tuist.swift
import ProjectDescription

let tuist = Tuist(
    plugins: [
        .git(
            url: "https://github.com/mycompany/tuist-plugin",
            tag: "1.0.0"
        ),
        // Yoki local path
        .local(path: "../TuistPluginMyCompany"),
    ]
)
```

```bash
tuist install  # Plugin'ni ham yuklaydi
```

---

## 4. Synthesized Files

Tuist ma'lum fayllarni **avtomatik generate** qila oladi.

### Bundle Accessors

Resource'larga kirish uchun avtomatik code yaratiladi:

```swift
// Tuist avtomatik yaratadi:
// Bundle+Module.swift
extension Bundle {
    static let module: Bundle = .init(for: BundleToken.self)
}

// Sizning kodingizda:
let image = UIImage(named: "logo", in: .module, compatibleWith: nil)
```

### Asset Accessors

```swift
// Tuist avtomatik yaratadi (SwiftGen kabi):
// Assets+Generated.swift
enum Asset {
    static let logo = ImageAsset(name: "logo")
    static let primaryColor = ColorAsset(name: "primaryColor")
}

// Kodda:
let image = Asset.logo.image
let color = Asset.primaryColor.color
```

### Sozlash

```swift
// Project.swift
let project = Project(
    name: "MyApp",
    resourceSynthesizers: [
        .assets(),        // Assets.xcassets uchun
        .strings(),       // Localizable.strings uchun
        .fonts(),         // Fontlar uchun
        .plists(),        // Plist fayllar uchun
    ],
    targets: [...]
)
```

---

## 5. Dynamic Configuration

Environment variable'lar bilan manifest'ni o'zgartirish:

```swift
// Project.swift yoki Helper'da
import ProjectDescription

// Environment dan o'qish
func resolveProductType() -> Product {
    if case let .string(value) = Environment.productType {
        switch value {
        case "static": return .staticFramework
        case "dynamic": return .framework
        default: return .framework
        }
    }
    return .framework
}

// Ishlatish
.target(
    name: "CoreModule",
    product: resolveProductType(),  // ← Dynamic!
    // ...
)
```

```bash
# Development (dynamic)
tuist generate

# Release (static)
TUIST_PRODUCT_TYPE=static tuist generate

# Staging
TUIST_API_URL=https://staging.example.com tuist generate
```

---

## 6. App Store Archive va Publish (Batafsil)

### 6.1 Tuist Loyihasini Archive Qilish

Tuist loyihasi **oddiy Xcode loyiha** sifatida archive qilinadi. Hech qanday farq yo'q!

#### Xcode UI orqali (oddiy usul)

```bash
# 1. Generate qiling
tuist install
tuist generate

# 2. Xcode ochiladi
# 3. Product → Archive
# 4. Organizer da: Distribute App → App Store Connect → Upload
```

#### Terminal orqali (CI/CD uchun)

```bash
#!/bin/bash
# build_and_upload.sh

# 1. Tuist generate
tuist install
tuist generate --no-open

# 2. Archive
xcodebuild archive \
    -workspace MyApp.xcworkspace \
    -scheme MyApp \
    -configuration Release \
    -destination "generic/platform=iOS" \
    -archivePath ./build/MyApp.xcarchive \
    CODE_SIGN_STYLE=Manual \
    PROVISIONING_PROFILE_SPECIFIER="MyApp Distribution" \
    CODE_SIGN_IDENTITY="Apple Distribution"

# 3. Export IPA
xcodebuild -exportArchive \
    -archivePath ./build/MyApp.xcarchive \
    -exportPath ./build/ipa \
    -exportOptionsPlist ./ExportOptions.plist

# 4. Upload to App Store Connect
xcrun altool --upload-app \
    -f ./build/ipa/MyApp.ipa \
    -t ios \
    --apiKey "YOUR_API_KEY_ID" \
    --apiIssuer "YOUR_ISSUER_ID"
```

### 6.2 ExportOptions.plist

```xml
<?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>signingStyle</key>
    <string>manual</string>
    <key>provisioningProfiles</key>
    <dict>
        <key>com.example.MyApp</key>
        <string>MyApp Distribution</string>
    </dict>
    <key>uploadBitcode</key>
    <false/>
    <key>uploadSymbols</key>
    <true/>
</dict>
</plist>
```

### 6.3 Signing Settings — Project.swift da

```swift
.target(
    name: "MyApp",
    // ...
    settings: .settings(
        base: [
            // Automatic signing (developer account)
            "CODE_SIGN_STYLE": "Automatic",
            "DEVELOPMENT_TEAM": "ABCDE12345",
        ],
        configurations: [
            .debug(
                name: "Debug",
                settings: [
                    "CODE_SIGN_IDENTITY": "Apple Development",
                ]
            ),
            .release(
                name: "Release",
                settings: [
                    "CODE_SIGN_IDENTITY": "Apple Distribution",
                    // Manual signing uchun:
                    // "CODE_SIGN_STYLE": "Manual",
                    // "PROVISIONING_PROFILE_SPECIFIER": "MyApp Distribution",
                ]
            ),
        ]
    )
)
```

### 6.4 Fastlane Bilan Integratsiya

```ruby
# Fastfile
default_platform(:ios)

platform :ios do
  desc "Build using Tuist and upload to App Store"
  lane :release do
    # 1. Tuist generate
    sh("cd .. && tuist install")
    sh("cd .. && tuist generate --no-open")

    # 2. Build
    build_app(
      workspace: "MyApp.xcworkspace",
      scheme: "MyApp",
      configuration: "Release",
      export_method: "app-store",
    )

    # 3. Upload
    upload_to_app_store(
      skip_metadata: true,
      skip_screenshots: true,
    )
  end
end
```

```bash
# Fastlane ishga tushirish
fastlane release
```

### 6.5 Xcode Cloud Bilan

```swift
// ci_scripts/ci_post_clone.sh (Xcode Cloud)
#!/bin/bash
set -e

# Tuist o'rnatish
brew tap tuist/tuist
brew install --formula tuist

# Dependencies va Generate
tuist install
tuist generate --no-open
```

> **Muhim:** Xcode Cloud `ci_scripts/ci_post_clone.sh` scriptini avtomatik ishga tushiradi clone'dan keyin.

---

## Xulosa

| Feature | Tushuntirish |
|---------|-------------|
| **ProjectDescriptionHelpers** | Manifest kodini DRY qilish |
| **Templates** | Yangi modul yaratishni avtomatlashtirish |
| **Plugins** | Loyihalar orasida config ulashish |
| **Synthesized Files** | Asset/String accessors avtomatik |
| **Dynamic Config** | Environment variable bilan manifest o'zgartirish |
| **App Store Archive** | Xcode UI yoki xcodebuild terminal bilan |
| **Fastlane/Xcode Cloud** | CI/CD bilan to'liq integratsiya |

---

> 💡 **Keyingi darslik:** [Real Loyiha #1: ToDo App](09_loyiha_todo.md) — Birinchi real loyihani Tuist bilan quramiz!

> *"Yaxshi vositalar yaxshi ustalar yaratadi. Tuist — sizning eng yaxshi vositangiz!"* 🛠️
