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
    }
}