Section 7/81 menit

7. Real Use Cases

7. Real Use Cases

Use Case 1: Multi-Parameter Validator

swift
import Foundation

// Protocol untuk semua validatable types
protocol Validatable {
    associatedtype Value
    func validate(_ value: Value) -> ValidationResult
}

struct ValidationResult {
    let isValid: Bool
    let fieldName: String
    let errorMessage: String?
    
    static func success(_ field: String) -> ValidationResult {
        ValidationResult(isValid: true, fieldName: field, errorMessage: nil)
    }
    
    static func failure(_ field: String, message: String) -> ValidationResult {
        ValidationResult(isValid: false, fieldName: field, errorMessage: message)
    }
}

struct StringValidator: Validatable {
    let fieldName: String
    let minLength: Int
    let maxLength: Int
    
    func validate(_ value: String) -> ValidationResult {
        if value.count < minLength {
            return .failure(fieldName, message: "Minimal \(minLength) karakter")
        }
        if value.count > maxLength {
            return .failure(fieldName, message: "Maksimal \(maxLength) karakter")
        }
        return .success(fieldName)
    }
}

struct RangeValidator<T: Comparable>: Validatable {
    let fieldName: String
    let range: ClosedRange<T>
    
    func validate(_ value: T) -> ValidationResult {
        range.contains(value)
            ? .success(fieldName)
            : .failure(fieldName, message: "Harus di antara \(range.lowerBound) dan \(range.upperBound)")
    }
}

// Form validation engine menggunakan pack
struct FormValidator {
    
    // Validate banyak field sekaligus dengan tipe berbeda
    static func validate<each V: Validatable>(
        pairs: repeat (each V, (each V).Value)
    ) -> [ValidationResult] {
        var results: [ValidationResult] = []
        for (validator, value) in repeat each pairs {
            results.append(validator.validate(value))
        }
        return results
    }
    
    static func allValid(_ results: [ValidationResult]) -> Bool {
        results.allSatisfy { $0.isValid }
    }
}

// Penggunaan:
struct RegistrationForm {
    var username: String
    var age: Int
    var bio: String
}

func validateRegistrationForm(_ form: RegistrationForm) -> [ValidationResult] {
    let usernameValidator = StringValidator(fieldName: "username", minLength: 3, maxLength: 30)
    let ageValidator = RangeValidator(fieldName: "age", range: 13...120)
    let bioValidator = StringValidator(fieldName: "bio", minLength: 0, maxLength: 500)
    
    return FormValidator.validate(pairs:
        (usernameValidator, form.username),
        (ageValidator, form.age),
        (bioValidator, form.bio)
    )
}

let form = RegistrationForm(username: "ari", age: 28, bio: "iOS Developer")
let results = validateRegistrationForm(form)
let errors = results.filter { !$0.isValid }
print(errors.isEmpty ? "Form valid!" : "Errors: \(errors.map { $0.errorMessage ?? "" })")

Use Case 2: Type-Safe Observable Properties

swift
// Observer pattern untuk multiple property types
protocol PropertyObserver: AnyObject {
    associatedtype Value
    func valueChanged(to newValue: Value)
}

class PropertyStore {
    
    // Notifikasi multiple observer dengan tipe berbeda sekaligus
    func notifyObservers<each O: PropertyObserver>(
        _ observers: repeat each O,
        with values: repeat (each O).Value
    ) {
        for (observer, value) in repeat each (observers, values) {
            observer.valueChanged(to: value)
        }
    }
}

// Konkret observer types
class IntObserver: PropertyObserver {
    var lastValue: Int?
    func valueChanged(to newValue: Int) { lastValue = newValue }
}

class StringObserver: PropertyObserver {
    var lastValue: String?
    func valueChanged(to newValue: String) { lastValue = newValue }
}

class BoolObserver: PropertyObserver {
    var lastValue: Bool?
    func valueChanged(to newValue: Bool) { lastValue = newValue }
}

// Penggunaan
let store = PropertyStore()
let countObserver = IntObserver()
let nameObserver = StringObserver()
let activeObserver = BoolObserver()

store.notifyObservers(
    countObserver, nameObserver, activeObserver,
    with: 42, "Ari", true
)

print(countObserver.lastValue!)   // 42
print(nameObserver.lastValue!)    // "Ari"
print(activeObserver.lastValue!)  // true

Use Case 3: Dependency Container

swift
// Type-safe dependency injection container
protocol Injectable {
    init()
}

class DependencyContainer {
    
    // Resolve multiple dependencies sekaligus, type-safe
    func resolve<each D: Injectable>(_ types: repeat (each D).Type) -> (repeat each D) {
        (repeat (each types).init())
    }
    
    // Make dan configure
    func make<each D: Injectable & Configurable>(
        _ types: repeat (each D).Type,
        configurations: repeat Configuration<each D>
    ) -> (repeat each D) {
        var instances = (repeat (each types).init())
        for (instance, config) in repeat each (instances, configurations) {
            // Ini tidak langsung bisa karena tuple mutation
            // Tapi konsepnya benar
        }
        return instances
    }
}

protocol Configurable {
    mutating func configure(with config: [String: Any])
}

struct Configuration<T> {
    let values: [String: Any]
}

// Services
struct UserService: Injectable {
    init() { print("UserService initialized") }
}

struct AuthService: Injectable {
    init() { print("AuthService initialized") }
}

struct AnalyticsService: Injectable {
    init() { print("AnalyticsService initialized") }
}

// Penggunaan
let container = DependencyContainer()

let (userService, authService, analyticsService) = container.resolve(
    UserService.self,
    AuthService.self,
    AnalyticsService.self
)
// Semua diinstansiasi, tipe-nya diketahui compiler

Use Case 4: Assertion Helper untuk Testing

swift
import Testing

// Type-safe assertion untuk banyak nilai sekaligus
func assertAll<each T: Equatable>(
    _ label: String,
    pairs: repeat (actual: each T, expected: each T),
    sourceLocation: SourceLocation = #_sourceLocation
) {
    var allPassed = true
    var failureMessages: [String] = []
    
    for (actual, expected) in repeat each pairs {
        if actual != expected {
            allPassed = false
            failureMessages.append("  Expected \(expected), got \(actual)")
        }
    }
    
    if !allPassed {
        let message = "\(label) assertion(s) failed:\n" + failureMessages.joined(separator: "\n")
        Issue.record(message, sourceLocation: sourceLocation)
    }
}

// Penggunaan dalam test
@Test("User creation sets all properties correctly")
func userCreation() {
    let user = User(name: "Ari", age: 28, email: "ari@example.com", isActive: true)
    
    // Assert banyak properti sekaligus, semua type-safe
    assertAll("User properties",
        pairs:
            (actual: user.name, expected: "Ari"),
            (actual: user.age, expected: 28),
            (actual: user.email, expected: "ari@example.com"),
            (actual: user.isActive, expected: true)
    )
    // Jika beberapa gagal, semua failure dilaporkan sekaligus
}

struct User {
    var name: String
    var age: Int
    var email: String
    var isActive: Bool
}