Section 17/181 menit
17. Real Use Cases
17. Real Use Cases
Use Case 1: Modular iOS App dengan Fitur-Based Packages
Perusahaan fintech dengan 5 tim fitur (Payments, Investments, Profile, Onboarding, Dashboard) yang berbagi design system dan networking layer.
swift
// Package.swift — DesignSystem package
import PackageDescription
let package = Package(
name: "DesignSystem",
platforms: [.iOS(.v16)],
products: [
.library(name: "DesignSystem", targets: ["DesignSystem"]),
.library(name: "DesignSystemTesting", targets: ["DesignSystemTesting"])
],
dependencies: [
.package(url: "https://github.com/airbnb/lottie-ios", from: "4.0.0")
],
targets: [
.target(
name: "DesignSystem",
dependencies: [
.product(name: "Lottie", package: "lottie-ios")
],
resources: [
.process("Resources/Assets.xcassets"),
.process("Resources/Fonts/"),
.process("Resources/Animations/"),
.process("Resources/en.lproj/"),
.process("Resources/id.lproj/")
]
),
// Test support library — mock components untuk testing tim lain
.target(
name: "DesignSystemTesting",
dependencies: ["DesignSystem"]
),
.testTarget(
name: "DesignSystemTests",
dependencies: ["DesignSystem", "DesignSystemTesting"]
)
]
)
swift
// Package.swift — Payments feature package
import PackageDescription
let package = Package(
name: "PaymentsFeature",
platforms: [.iOS(.v16)],
products: [
.library(name: "PaymentsFeature", targets: ["PaymentsFeature"])
],
dependencies: [
// Internal packages via local path (dalam workspace)
.package(path: "../../Core/DesignSystem"),
.package(path: "../../Core/NetworkLayer"),
.package(path: "../../Core/CoreDomain"),
// External
.package(url: "https://github.com/apple/swift-collections", from: "1.0.0")
],
targets: [
.target(
name: "PaymentsFeature",
dependencies: [
.product(name: "DesignSystem", package: "DesignSystem"),
.product(name: "NetworkLayer", package: "NetworkLayer"),
.product(name: "CoreDomain", package: "CoreDomain"),
.product(name: "Collections", package: "swift-collections")
]
),
.testTarget(
name: "PaymentsFeatureTests",
dependencies: [
"PaymentsFeature",
.product(name: "DesignSystemTesting", package: "DesignSystem"),
.product(name: "NetworkLayerTesting", package: "NetworkLayer")
]
)
]
)
swift
// Sources/PaymentsFeature/PaymentCoordinator.swift
import DesignSystem
import NetworkLayer
import CoreDomain
import UIKit
@MainActor
public final class PaymentCoordinator {
private let navigationController: UINavigationController
private let apiClient: APIClient
public init(navigationController: UINavigationController, apiClient: APIClient) {
self.navigationController = navigationController
self.apiClient = apiClient
}
public func start() {
let viewModel = PaymentListViewModel(apiClient: apiClient)
let viewController = PaymentListViewController(viewModel: viewModel)
navigationController.pushViewController(viewController, animated: true)
}
}
Use Case 2: OpenAPI Code Generation dengan Build Tool Plugin
Sebuah app yang berkomunikasi dengan backend via OpenAPI spec, dengan code generation otomatis.
swift
// Package.swift — OpenAPIGeneratorPlugin
import PackageDescription
let package = Package(
name: "OpenAPIGeneratorPlugin",
products: [
.plugin(name: "OpenAPIGeneratorPlugin", targets: ["OpenAPIGeneratorPlugin"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-openapi-generator", from: "1.0.0")
],
targets: [
.plugin(
name: "OpenAPIGeneratorPlugin",
capability: .buildTool(),
dependencies: [
.product(name: "swift-openapi-generator", package: "swift-openapi-generator")
]
)
]
)
swift
// Sources/OpenAPIGeneratorPlugin/OpenAPIGeneratorPlugin.swift
import PackagePlugin
@main
struct OpenAPIGeneratorPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
guard let sourceTarget = target as? SourceModuleTarget else { return [] }
let generator = try context.tool(named: "swift-openapi-generator")
let outputDir = context.pluginWorkDirectory.appending("GeneratedSources")
// Cari openapi.yaml atau openapi.json di target
let specFiles = sourceTarget.sourceFiles.filter { file in
file.path.lastComponent == "openapi.yaml" ||
file.path.lastComponent == "openapi.json"
}
return specFiles.map { specFile in
Command.buildCommand(
displayName: "Generating API client from \(specFile.path.lastComponent)",
executable: generator.path,
arguments: [
"generate",
"--input-file", specFile.path.string,
"--output-directory", outputDir.string,
"--mode", "client",
"--naming-strategy", "idiomatic"
],
inputFiles: [specFile.path],
outputFiles: [
outputDir.appending("Client.swift"),
outputDir.appending("Types.swift")
]
)
}
}
}
swift
// Packages/NetworkLayer/Package.swift — menggunakan plugin
import PackageDescription
let package = Package(
name: "NetworkLayer",
platforms: [.iOS(.v16), .macOS(.v13)],
products: [
.library(name: "NetworkLayer", targets: ["NetworkLayer"]),
.library(name: "NetworkLayerTesting", targets: ["NetworkLayerTesting"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.0.0"),
.package(path: "../../Tools/OpenAPIGeneratorPlugin")
],
targets: [
.target(
name: "NetworkLayer",
dependencies: [
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime")
],
plugins: [
// Plugin otomatis generate dari openapi.yaml di Sources/NetworkLayer/
.plugin(name: "OpenAPIGeneratorPlugin", package: "OpenAPIGeneratorPlugin")
]
),
.target(
name: "NetworkLayerTesting",
dependencies: ["NetworkLayer"]
),
.testTarget(
name: "NetworkLayerTests",
dependencies: ["NetworkLayer", "NetworkLayerTesting"]
)
]
)
swift
# Sources/NetworkLayer/openapi.yaml — file ini yang di-generate
openapi: "3.0.0"
info:
title: Payment API
version: "1.0.0"
paths:
/payments:
get:
operationId: listPayments
parameters:
- name: page
in: query
schema:
type: integer
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentList'
/payments/{id}:
get:
operationId: getPayment
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
content:
application/json:
schema:
$ref: '#/components/schemas/Payment'
components:
schemas:
Payment:
type: object
required: [id, amount, currency, status]
properties:
id:
type: string
amount:
type: number
currency:
type: string
status:
type: string
enum: [pending, completed, failed]
PaymentList:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/Payment'
total:
type: integer
swift
// Sources/NetworkLayer/PaymentAPIClient.swift — menggunakan generated code
import OpenAPIRuntime
import Foundation
// Client, Types — di-generate otomatis dari openapi.yaml setiap build
public actor PaymentAPIClient {
private let client: Client
public init(serverURL: URL, token: String) throws {
self.client = Client(
serverURL: serverURL,
transport: URLSessionTransport(),
middlewares: [
AuthenticationMiddleware(token: token)
]
)
}
public func listPayments(page: Int = 1) async throws -> [Payment] {
let response = try await client.listPayments(
query: .init(page: page)
)
switch response {
case .ok(let body):
let list = try body.body.json
return list.items?.compactMap(Payment.init) ?? []
}
}
public func getPayment(id: String) async throws -> Payment {
let response = try await client.getPayment(
path: .init(id: id)
)
switch response {
case .ok(let body):
return try Payment(body.body.json)
}
}
}
public struct Payment: Sendable {
public let id: String
public let amount: Double
public let currency: String
public let status: Status
public enum Status: String, Sendable {
case pending, completed, failed
}
}