08. Code Sharing va App Store
🚀 Tuist — Mukammal Qo‘llanma & Amaliyot
🔧 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
// 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
// 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
// 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
mkdir -p Tuist/Templates/Feature
// 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
// 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")
}
}
// 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
# 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)
// Plugin.swift
import ProjectDescription
let plugin = Plugin(name: "MyCompanyPlugin")
Plugin'ni Ishlatish
// 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"),
]
)
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:
// 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
// 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
// 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:
// 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!
// ...
)
# 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)
# 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)
#!/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 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
.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
# 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
# Fastlane ishga tushirish
fastlane release
6.5 Xcode Cloud Bilan
// 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.shscriptini 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 — Birinchi real loyihani Tuist bilan quramiz!
"Yaxshi vositalar yaxshi ustalar yaratadi. Tuist — sizning eng yaxshi vositangiz!" 🛠️