Section 7/171 menit
7. Scaffolding Templates
7. Scaffolding Templates
Membuat Template Module
Scaffolding Tuist memungkinkan generate kode boilerplate secara konsisten dari template:
swift
Tuist/
└── Templates/
├── feature/
│ ├── template.swift ← definisi template
│ └── Sources/
│ └── ___NAME___View.swift.stencil ← file template
└── core/
├── template.swift
└── Sources/
└── ___NAME___Service.swift.stencil
template.swift untuk Feature Module
swift
// Tuist/Templates/feature/template.swift
import ProjectDescription
let nameAttribute: Template.Attribute = .required("name")
let authorAttribute: Template.Attribute = .optional("author", default: "Developer")
let template = Template(
description: "Feature module template dengan View + ViewModel + Tests",
attributes: [nameAttribute, authorAttribute],
items: [
// Project.swift
.file(
path: "Projects/Features/Feature\(nameAttribute)/Project.swift",
templatePath: "Project.swift.stencil"
),
// Main source file
.file(
path: "Projects/Features/Feature\(nameAttribute)/Sources/Feature\(nameAttribute)/Feature\(nameAttribute)View.swift",
templatePath: "Sources/FeatureView.swift.stencil"
),
// ViewModel
.file(
path: "Projects/Features/Feature\(nameAttribute)/Sources/Feature\(nameAttribute)/Feature\(nameAttribute)ViewModel.swift",
templatePath: "Sources/FeatureViewModel.swift.stencil"
),
// Test file
.file(
path: "Projects/Features/Feature\(nameAttribute)/Tests/Feature\(nameAttribute)Tests/Feature\(nameAttribute)ViewModelTests.swift",
templatePath: "Tests/FeatureViewModelTests.swift.stencil"
),
// Workspace update (directory creation)
.directory(
path: "Projects/Features/Feature\(nameAttribute)/Resources",
sourcePath: "Resources"
),
]
)
Stencil Template Files
swift
// Tuist/Templates/feature/Sources/FeatureView.swift.stencil
// Template menggunakan Stencil syntax: {{ name }}, {% if ... %}
import SwiftUI
// Feature: {{ name }}
// Author: {{ author }}
public struct Feature{{ name }}View: View {
@StateObject private var viewModel = Feature{{ name }}ViewModel()
public init() {}
public var body: some View {
NavigationView {
VStack {
if viewModel.isLoading {
ProgressView()
} else {
content
}
}
.navigationTitle("{{ name }}")
.task {
await viewModel.onAppear()
}
}
}
@ViewBuilder
private var content: some View {
Text("Feature {{ name }}")
}
}
#Preview {
Feature{{ name }}View()
}
swift
// Tuist/Templates/feature/Sources/FeatureViewModel.swift.stencil
import Foundation
import Observation
@MainActor
@Observable
public final class Feature{{ name }}ViewModel {
public private(set) var isLoading = false
public private(set) var errorMessage: String?
public init() {}
public func onAppear() async {
isLoading = true
defer { isLoading = false }
do {
try await loadData()
} catch {
errorMessage = error.localizedDescription
}
}
private func loadData() async throws {
// TODO: implement
try await Task.sleep(for: .milliseconds(500))
}
}
swift
// Tuist/Templates/feature/Project.swift.stencil
import ProjectDescription
import ProjectDescriptionHelpers
let project = Project.module(
name: "Feature{{ name }}",
layer: .feature,
dependencies: [
.core("NetworkKit"),
.shared("SharedUI"),
],
hasExampleApp: false
)
Menggunakan Template
swift
# Generate feature module baru
tuist scaffold feature --name Payment --author "Alice"
# Output:
# ✅ Projects/Features/FeaturePayment/Project.swift
# ✅ Projects/Features/FeaturePayment/Sources/FeaturePayment/FeaturePaymentView.swift
# ✅ Projects/Features/FeaturePayment/Sources/FeaturePayment/FeaturePaymentViewModel.swift
# ✅ Projects/Features/FeaturePayment/Tests/FeaturePaymentTests/FeaturePaymentViewModelTests.swift
# ✅ Projects/Features/FeaturePayment/Resources/
# Tambahkan ke Workspace.swift
# Jalankan tuist generate
# Langsung coding
Template untuk Core Module
swift
// Tuist/Templates/core/template.swift
import ProjectDescription
let nameAttribute: Template.Attribute = .required("name")
let template = Template(
description: "Core module template (service/repository pattern)",
attributes: [nameAttribute],
items: [
.file(
path: "Projects/Core/\(nameAttribute)/Project.swift",
templatePath: "Project.swift.stencil"
),
.file(
path: "Projects/Core/\(nameAttribute)/Sources/\(nameAttribute)/\(nameAttribute)Service.swift",
templatePath: "Sources/Service.swift.stencil"
),
.file(
path: "Projects/Core/\(nameAttribute)/Sources/\(nameAttribute)/\(nameAttribute)ServiceProtocol.swift",
templatePath: "Sources/ServiceProtocol.swift.stencil"
),
.file(
path: "Projects/Core/\(nameAttribute)/Tests/\(nameAttribute)Tests/\(nameAttribute)ServiceTests.swift",
templatePath: "Tests/ServiceTests.swift.stencil"
),
]
)