Ejemplos de Manejo de Errores macOS Swift

Ejemplos de manejo de errores macOS Swift incluyendo captura de excepciones, registro y validación de parámetros

💻 Captura de Excepciones swift

🟢 simple ⭐⭐

Manejar errores usando bloques do-catch, patrones try-catch y tipos de error personalizados

⏱️ 25 min 🏷️ swift, macos, error
Prerequisites: Basic Swift, Error types
// macOS Swift Exception Catching Examples
// Error handling with do-catch, throws, and custom errors

import Foundation

// 1. Define Custom Error Types
enum NetworkError: Error, LocalizedError {
    case invalidURL
    case connectionFailed
    case timeout
    case serverError(code: Int)

    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return "The provided URL is invalid"
        case .connectionFailed:
            return "Failed to establish network connection"
        case .timeout:
            return "Request timed out"
        case .serverError(let code):
            return "Server returned error code: \(code)"
        }
    }
}

enum ValidationError: Error {
    case emptyInput
    case invalidFormat
    case outOfRange(min: Int, max: Int, value: Int)
    case tooShort(length: Int, minLength: Int)

    var localizedDescription: String {
        switch self {
        case .emptyInput:
            return "Input cannot be empty"
        case .invalidFormat:
            return "Input format is invalid"
        case .outOfRange(let min, let max, let value):
            return "Value \(value) is out of range [\(min), \(max)]"
        case .tooShort(let length, let minLength):
            return "Input length \(length) is too short (minimum: \(minLength))"
        }
    }
}

// 2. Basic do-catch Error Handling
class BasicErrorHandling {

    func divide(_ a: Int, by b: Int) throws -> Double {
        guard b != 0 else {
            throw NSError(domain: "MathError", code: 1, userInfo: [
                NSLocalizedDescriptionKey: "Division by zero is not allowed"
            ])
        }
        return Double(a) / Double(b)
    }

    func demonstrateBasicCatch() {
        print("\n--- Basic do-catch Error Handling ---")

        do {
            let result = try divide(10, by: 2)
            print("10 / 2 = \(result)")
        } catch {
            print("Error: \(error.localizedDescription)")
        }

        // Error case
        do {
            let result = try divide(10, by: 0)
            print("Result: \(result)")
        } catch {
            print("Caught error: \(error.localizedDescription)")
        }
    }
}

// 3. Multiple Catch Blocks
class MultipleCatchBlocks {

    func processOperation(value: Int) throws -> String {
        if value < 0 {
            throw NetworkError.invalidURL
        } else if value == 0 {
            throw ValidationError.emptyInput
        }
        return "Success: \(value)"
    }

    func demonstrateMultipleCatches() {
        print("\n--- Multiple Catch Blocks ---")

        let values = [-1, 0, 5]

        for value in values {
            do {
                let result = try processOperation(value: value)
                print(result)
            } catch let error as NetworkError {
                print("Network error: \(error.errorDescription ?? "Unknown")")
            } catch let error as ValidationError {
                print("Validation error: \(error.localizedDescription)")
            } catch {
                print("Generic error: \(error.localizedDescription)")
            }
        }
    }
}

// 4. try? - Convert to Optional
class OptionalTry {

    func riskyOperation(success: Bool) throws -> String {
        if success {
            return "Operation succeeded"
        } else {
            throw NSError(domain: "Error", code: 1)
        }
    }

    func demonstrateOptionalTry() {
        print("\n--- try? - Convert to Optional ---")

        // Success case - returns Optional("Operation succeeded")
        let result1 = try? riskyOperation(success: true)
        print("Result 1: \(result1 ?? "nil")")

        // Failure case - returns nil instead of throwing
        let result2 = try? riskyOperation(success: false)
        print("Result 2: \(result2?.description ?? "nil")")

        // Chaining with nil coalescing
        let finalResult = (try? riskyOperation(success: false)) ?? "Default value"
        print("Final result: \(finalResult)")
    }
}

// 5. try! - Force Unwrap
class ForceTry {

    func forceUnwrapDemo() {
        print("\n--- try! - Force Unwrap ---")

        // This works because operation succeeds
        let result1 = try! riskyOperation(success: true)
        print("Force unwrap success: \(result1)")

        // This would crash because operation fails
        do {
            // let result2 = try! riskyOperation(success: false)  // CRASH!
            // print("Never reached")
        }

        print("Use try! only when you're certain the operation won't fail")
    }

    private func riskyOperation(success: Bool) throws -> String {
        return success ? "Success" : "Failure"
    }
}

// 6. defer Statement
class DeferExample {

    func processFile(path: String) throws -> String {
        print("\n--- Defer Statement ---")
        print("Opening file: \(path)")

        // defer will execute when scope exits
        defer {
            print("Closing file: \(path)")
        }

        // Simulate processing
        print("Processing file...")

        if path.isEmpty {
            throw ValidationError.emptyInput
        }

        return "File processed successfully"
    }

    func demonstrateDefer() {
        do {
            let result = try processFile(path: "/path/to/file.txt")
            print(result)
        } catch {
            print("Error: \(error)")
        }

        // Error case - defer still executes
        do {
            let result = try processFile(path: "")
            print(result)
        } catch {
            print("Error caught: \(error)")
        }
    }

    // Multiple defer blocks (LIFO order)
    func multipleDefer() {
        print("\n--- Multiple Defer Blocks ---")

        defer { print("Defer 1") }
        defer { print("Defer 2") }
        defer { print("Defer 3") }

        print("Main code")
    }
}

// 7. Rethrowing Functions
class RethrowingExample {

    func transform<T>(_ value: T, using transform: (T) throws -> String) rethrows -> String {
        print("\n--- Rethrowing Function ---")
        return try transform(value)
    }

    func demonstrateRethrow() {
        do {
            let result = try transform(42) { value in
                if value < 0 {
                    throw ValidationError.invalidFormat
                }
                return "Transformed: \(value)"
            }
            print(result)
        } catch {
            print("Error: \(error)")
        }
    }
}

// 8. Error Propagation
class ErrorPropagation {

    func step1() throws -> String {
        print("Step 1")
        return "Step1"
    }

    func step2(input: String) throws -> String {
        print("Step 2 with: \(input)")
        if input.isEmpty {
            throw ValidationError.emptyInput
        }
        return input + " -> Step2"
    }

    func step3(input: String) throws -> String {
        print("Step 3 with: \(input)")
        return input + " -> Step3"
    }

    func executePipeline() throws -> String {
        print("\n--- Error Propagation ---")

        let result1 = try step1()
        let result2 = try step2(input: result1)
        let result3 = try step3(input: result2)

        return result3
    }

    func demonstratePropagation() {
        do {
            let result = try executePipeline()
            print("Pipeline result: \(result)")
        } catch {
            print("Pipeline failed: \(error)")
        }
    }
}

// 9. Result Type for Error Handling
class ResultTypeExample {

    func divideResult(_ a: Int, by b: Int) -> Result<Double, Error> {
        guard b != 0 else {
            return .failure(NSError(domain: "MathError", code: 1, userInfo: [
                NSLocalizedDescriptionKey: "Division by zero"
            ]))
        }
        return .success(Double(a) / Double(b))
    }

    func demonstrateResult() {
        print("\n--- Result Type ---")

        let result1 = divideResult(10, by: 2)

        switch result1 {
        case .success(let value):
            print("10 / 2 = \(value)")
        case .failure(let error):
            print("Error: \(error)")
        }

        // Error case
        let result2 = divideResult(10, by: 0)

        switch result2 {
        case .success(let value):
            print("Result: \(value)")
        case .failure(let error):
            print("Error: \(error.localizedDescription)")
        }

        // Using get() method
        if let value = try? divideResult(20, by: 4).get() {
            print("Direct get: \(value)")
        }
    }
}

// 10. Comprehensive Error Handler
class ComprehensiveErrorHandler {

    enum AppError: Error {
        case network(NetworkError)
        case validation(ValidationError)
        case unknown(Error)

        var localizedDescription: String {
            switch self {
            case .network(let error):
                return "Network: \(error.errorDescription ?? "Unknown")"
            case .validation(let error):
                return "Validation: \(error.localizedDescription)"
            case .unknown(let error):
                return "Unknown: \(error.localizedDescription)"
            }
        }
    }

    func handleOperation(operation: () throws -> Void) {
        print("\n--- Comprehensive Error Handling ---")

        do {
            try operation()
            print("Operation completed successfully")
        } catch let error as NetworkError {
            let appError = AppError.network(error)
            handleAppError(appError)
        } catch let error as ValidationError {
            let appError = AppError.validation(error)
            handleAppError(appError)
        } catch {
            let appError = AppError.unknown(error)
            handleAppError(appError)
        }
    }

    private func handleAppError(_ error: AppError) {
        print("Error occurred: \(error.localizedDescription)")

        switch error {
        case .network:
            print("Suggested action: Check your internet connection")
        case .validation:
            print("Suggested action: Please check your input")
        case .unknown:
            print("Suggested action: Please try again later")
        }
    }

    func demonstrateComprehensive() {
        // Success case
        handleOperation {
            print("Executing operation...")
        }

        // Network error
        handleOperation {
            throw NetworkError.connectionFailed
        }

        // Validation error
        handleOperation {
            throw ValidationError.invalidFormat
        }
    }
}

// 11. Async Error Handling
class AsyncErrorHandling {

    func fetchData() async throws -> String {
        print("\n--- Async Error Handling ---")

        // Simulate async operation
        try await Task.sleep(nanoseconds: 100_000_000) // 0.1 seconds

        let success = true

        if success {
            return "Data fetched successfully"
        } else {
            throw NetworkError.timeout
        }
    }

    func demonstrateAsync() async {
        do {
            let result = try await fetchData()
            print(result)
        } catch {
            print("Async error: \(error)")
        }
    }
}

// 12. Error Logging with Context
class ErrorWithContext {

    struct ErrorContext {
        let operation: String
        let timestamp: Date
        let additionalInfo: [String: Any]

        var description: String {
            let info = additionalInfo.map { "\($0.key): \($0.value)" }.joined(separator: ", ")
            return "[\(timestamp)] Operation '\(operation)' failed. Info: \(info)"
        }
    }

    enum ContextualError: Error {
        case operationFailed(context: ErrorContext)
        case validationFailed(context: ErrorContext)

        var localizedDescription: String {
            switch self {
            case .operationFailed(let context):
                return "Operation failed - " + context.description
            case .validationFailed(let context):
                return "Validation failed - " + context.description
            }
        }
    }

    func performOperation(userId: String, value: Int) throws {
        print("\n--- Error with Context ---")

        guard !userId.isEmpty else {
            let context = ErrorContext(
                operation: "performOperation",
                timestamp: Date(),
                additionalInfo: ["userId": userId, "reason": "empty"]
            )
            throw ContextualError.validationFailed(context: context)
        }

        guard value > 0 else {
            let context = ErrorContext(
                operation: "performOperation",
                timestamp: Date(),
                additionalInfo: ["userId": userId, "value": value]
            )
            throw ContextualError.operationFailed(context: context)
        }

        print("Operation succeeded for user \(userId) with value \(value)")
    }

    func demonstrateContext() {
        do {
            try performOperation(userId: "user123", value: 10)
        } catch let error as ContextualError {
            print("Error: \(error.localizedDescription)")
        }

        do {
            try performOperation(userId: "", value: 10)
        } catch let error as ContextualError {
            print("Error: \(error.localizedDescription)")
        }
    }
}

// Main demonstration
func demonstrateExceptionCatching() {
    print("=== macOS Swift Exception Catching Examples ===")

    // 1. Basic error handling
    let basic = BasicErrorHandling()
    basic.demonstrateBasicCatch()

    // 2. Multiple catch blocks
    let multiCatch = MultipleCatchBlocks()
    multiCatch.demonstrateMultipleCatches()

    // 3. Optional try
    let optionalTry = OptionalTry()
    optionalTry.demonstrateOptionalTry()

    // 4. Force try
    let forceTry = ForceTry()
    forceTry.forceUnwrapDemo()

    // 5. Defer
    let deferExample = DeferExample()
    deferExample.demonstrateDefer()
    deferExample.multipleDefer()

    // 6. Rethrowing
    let rethrow = RethrowingExample()
    rethrow.demonstrateRethrow()

    // 7. Error propagation
    let propagation = ErrorPropagation()
    propagation.demonstratePropagation()

    // 8. Result type
    let resultType = ResultTypeExample()
    resultType.demonstrateResult()

    // 9. Comprehensive handler
    let comprehensive = ComprehensiveErrorHandler()
    comprehensive.demonstrateComprehensive()

    // 10. Error with context
    let context = ErrorWithContext()
    context.demonstrateContext()

    print("\n=== All Exception Catching Examples Completed ===")
}

// Run demonstration
demonstrateExceptionCatching()

💻 Registro de Logs swift

🟡 intermediate ⭐⭐⭐

Implementar sistema integral de registro con salida a archivo, niveles y rotación

⏱️ 30 min 🏷️ swift, macos, logging
Prerequisites: Intermediate Swift, File I/O
// macOS Swift Logging Examples
// Comprehensive logging with file output, levels, and rotation

import Foundation
import OSLog

// 1. Simple Print Logging
class SimpleLogger {

    func log(message: String) {
        print("[LOG] \(message)")
    }

    func logInfo(message: String) {
        print("[INFO] \(message)")
    }

    func logWarning(message: String) {
        print("[WARNING] \(message)")
    }

    func logError(message: String) {
        print("[ERROR] \(message)")
    }

    func demonstrateSimpleLogging() {
        print("\n--- Simple Print Logging ---")
        logInfo(message: "Application started")
        logWarning(message: "This is a warning")
        logError(message: "An error occurred")
        log(message: "Custom log message")
    }
}

// 2. Custom Logger with Levels
enum LogLevel: String, Comparable {
    case debug = "DEBUG"
    case info = "INFO"
    case warning = "WARNING"
    case error = "ERROR"
    case critical = "CRITICAL"

    static func < (lhs: LogLevel, rhs: LogLevel) -> Bool {
        let order: [LogLevel] = [.debug, .info, .warning, .error, .critical]
        return order.firstIndex(of: lhs)! < order.firstIndex(of: rhs)!
    }
}

struct LogEntry {
    let timestamp: Date
    let level: LogLevel
    let message: String
    let file: String
    let function: String
    let line: Int

    var formatted: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        let timeString = formatter.string(from: timestamp)

        let fileName = URL(fileURLWithPath: file).lastPathComponent

        return "[\(timeString)] [\(level.rawValue)] [\(fileName):\(line)] \(function): \(message)"
    }
}

class CustomLogger {
    static let shared = CustomLogger()

    private var logs: [LogEntry] = []
    private var minimumLevel: LogLevel = .debug
    private let lock = NSLock()

    private init() {}

    func setMinimumLevel(_ level: LogLevel) {
        lock.lock()
        defer { lock.unlock() }
        minimumLevel = level
    }

    func log(_ level: LogLevel, message: String, file: String = #file, function: String = #function, line: Int = #line) {
        guard level >= minimumLevel else { return }

        let entry = LogEntry(
            timestamp: Date(),
            level: level,
            message: message,
            file: file,
            function: function,
            line: line
        )

        lock.lock()
        logs.append(entry)
        lock.unlock()

        print(entry.formatted)
    }

    func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(.debug, message: message, file: file, function: function, line: line)
    }

    func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(.info, message: message, file: file, function: function, line: line)
    }

    func warning(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(.warning, message: message, file: file, function: function, line: line)
    }

    func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(.error, message: message, file: file, function: function, line: line)
    }

    func critical(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
        log(.critical, message: message, file: file, function: function, line: line)
    }

    func getLogs() -> [LogEntry] {
        lock.lock()
        defer { lock.unlock() }
        return logs
    }

    func clearLogs() {
        lock.lock()
        defer { lock.unlock() }
        logs.removeAll()
    }
}

// 3. File Logger
class FileLogger {
    private let fileURL: URL
    private let fileHandle: FileHandle?
    private let dateFormatter: DateFormatter

    init?(filename: String = "app.log") {
        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        fileURL = documentsPath.appendingPathComponent(filename)

        // Create file if it doesn't exist
        if !FileManager.default.fileExists(atPath: fileURL.path) {
            FileManager.default.createFile(atPath: fileURL.path, contents: nil, attributes: nil)
        }

        // Open file handle
        do {
            fileHandle = try FileHandle(forWritingTo: fileURL)
            fileHandle?.seekToEndOfFile()
        } catch {
            print("Failed to open log file: \(error)")
            return nil
        }

        dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
    }

    deinit {
        fileHandle?.closeFile()
    }

    func log(level: LogLevel, message: String) {
        let timestamp = dateFormatter.string(from: Date())
        let logLine = "[\(timestamp)] [\(level.rawValue)] \(message)\n"

        if let data = logLine.data(using: .utf8) {
            fileHandle?.write(data)
        }

        // Also print to console
        print(logLine, terminator: "")
    }

    func readLogEntries(limit: Int = 100) -> [String] {
        guard let content = try? String(contentsOf: fileURL) else {
            return []
        }

        let lines = content.components(separatedBy: .newlines)
        return Array(lines.suffix(limit))
    }

    func clearLogFile() {
        try? "".write(to: fileURL, atomically: true, encoding: .utf8)
        print("Log file cleared")
    }
}

// 4. Rotating File Logger
class RotatingFileLogger {
    private let baseURL: URL
    private let maxFileSize: Int64
    private let maxLogFiles: Int
    private var currentFileLogger: FileLogger?

    init(baseFilename: String = "app", maxFileSize: Int64 = 1024 * 1024, maxLogFiles: Int = 5) {
        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        baseURL = documentsPath
        self.maxFileSize = maxFileSize
        self.maxLogFiles = maxLogFiles

        openNewLogFile()
    }

    private func openNewLogFile() {
        let timestamp = DateFormatter().string(from: Date()).replacingOccurrences(of: " ", with: "_")
        let filename = "app_\(timestamp).log"

        currentFileLogger = FileLogger(filename: filename)
        print("Opened new log file: \(filename)")
    }

    private func rotateLogFiles() {
        let fileManager = FileManager.default

        // Get existing log files
        do {
            let files = try fileManager.contentsOfDirectory(at: baseURL, includingPropertiesForKeys: [.fileSizeKey], options: [])

            let logFiles = files.filter { $0.pathExtension == "log" }
                .sorted { $0.lastPathComponent > $1.lastPathComponent }

            // Remove old files if exceeding limit
            if logFiles.count > maxLogFiles {
                for i in maxLogFiles..<logFiles.count {
                    try? fileManager.removeItem(at: logFiles[i])
                    print("Removed old log file: \(logFiles[i].lastPathComponent)")
                }
            }
        } catch {
            print("Error rotating log files: \(error)")
        }
    }

    func log(level: LogLevel, message: String) {
        // Check if current file size exceeds limit
        if let fileURL = currentFileLogger?.fileURL,
           let attributes = try? FileManager.default.attributesOfItem(atPath: fileURL.path),
           let fileSize = attributes[.size] as? Int64,
           fileSize > maxFileSize {

            currentFileLogger = nil
            rotateLogFiles()
            openNewLogFile()
        }

        currentFileLogger?.log(level: level, message: message)
    }
}

// 5. Unified Logging (OSLog)
class OSLogLogger {
    private let logger = Logger(subsystem: "com.example.app", category: "General")

    init() {
        print("\n--- OSLog / Unified Logging ---")
    }

    func logDebug(_ message: String) {
        logger.debug("\(message)")
    }

    func logInfo(_ message: String) {
        logger.info("\(message)")
    }

    func logError(_ message: String) {
        logger.error("\(message)")
    }

    func logFault(_ message: String) {
        logger.fault("\(message)")
    }

    // With privacy
    func logWithPrivacy(_ message: String, userId: String) {
        logger.log("User \(privacy: .public(userId)) performed action: \(message)")
    }

    func demonstrateOSLog() {
        logDebug("This is a debug message")
        logInfo("Application started")
        logError("An error occurred")
        logFault("Critical failure!")

        logWithPrivacy("Logged in", userId: "user123")
        print("Check Console.app for OSLog messages")
    }
}

// 6. Structured Logging
struct StructuredLogEntry: Encodable {
    let timestamp: Date
    let level: String
    let message: String
    let context: [String: Any]

    enum CodingKeys: String, CodingKey {
        case timestamp, level, message, context
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(ISO8601DateFormatter().string(from: timestamp), forKey: .timestamp)
        try container.encode(level, forKey: .level)
        try container.encode(message, forKey: .message)

        let contextData = try JSONSerialization.data(withJSONObject: context)
        try container.encode(String(data: contextData, encoding: .utf8), forKey: .context)
    }
}

class StructuredLogger {
    private let entries: [StructuredLogEntry] = []
    private let lock = NSLock()

    func log(level: LogLevel, message: String, context: [String: Any] = [:]) {
        var logContext = context
        logContext["thread"] = Thread.current.isMainThread ? "main" : "background"
        logContext["processId"] = ProcessInfo.processInfo.processIdentifier

        let entry = StructuredLogEntry(
            timestamp: Date(),
            level: level.rawValue,
            message: message,
            context: logContext
        )

        lock.lock()
        defer { lock.unlock() }

        // In real implementation, would store entry
        print("Structured: \(entry)")
    }

    func logRequest(endpoint: String, statusCode: Int, duration: TimeInterval) {
        log(level: .info, message: "API Request completed", context: [
            "endpoint": endpoint,
            "statusCode": statusCode,
            "duration_ms": duration * 1000
        ])
    }
}

// 7. Performance Logger
class PerformanceLogger {
    private var startTimes: [String: Date] = [:]
    private let logger = CustomLogger.shared

    func startOperation(_ name: String) {
        startTimes[name] = Date()
        logger.debug("Operation '\(name)' started")
    }

    func endOperation(_ name: String) {
        guard let startTime = startTimes[name] else {
            logger.warning("Operation '\(name)' was not started")
            return
        }

        let duration = Date().timeIntervalSince(startTime)
        startTimes.removeValue(forKey: name)

        logger.info("Operation '\(name)' completed in \(String(format: "%.3f", duration))s")
    }

    func measure<T>(_ name: String, operation: () -> T) -> T {
        startOperation(name)
        let result = operation()
        endOperation(name)
        return result
    }

    async func measureAsync<T>(_ name: String, operation: () async throws -> T) async rethrows -> T {
        startOperation(name)
        let result = try await operation()
        endOperation(name)
        return result
    }
}

// 8. Error Logger
class ErrorLogger {
    private let logger = CustomLogger.shared
    private let fileLogger: FileLogger?

    init() {
        fileLogger = FileLogger(filename: "errors.log")
    }

    func logError(_ error: Error, context: [String: Any] = [:], file: String = #file, function: String = #function, line: Int = #line) {
        let fileName = URL(fileURLWithPath: file).lastPathComponent

        var message = "Error in \(fileName):\(line) \(function): \(error.localizedDescription)"
        if !context.isEmpty {
            let contextString = context.map { "\($0.key)=\($0.value)" }.joined(separator: ", ")
            message += " | Context: \(contextString)"
        }

        logger.error(message)
        fileLogger?.log(level: .error, message: message)
    }

    func logException(_ exception: NSException) {
        let message = "Exception: \(exception.name) - \(exception.reason ?? "No reason")"
        logger.critical(message)
        fileLogger?.log(level: .critical, message: message)

        if let stackTrace = exception.callStackSymbols.joined(separator: "\n") as String? {
            fileLogger?.log(level: .critical, message: "Stack trace:\n\(stackTrace)")
        }
    }
}

// 9. Network Request Logger
class NetworkLogger {
    private let logger = CustomLogger.shared

    func logRequest(url: String, method: String, headers: [String: String] = [:], body: Data? = nil) {
        logger.info("API Request: \(method) \(url)")

        if !headers.isEmpty {
            logger.debug("Headers: \(headers)")
        }

        if let body = body, let jsonString = try? JSONSerialization.jsonObject(with: body, options: []) {
            logger.debug("Body: \(jsonString)")
        }
    }

    func logResponse(url: String, statusCode: Int, headers: [String: String] = [:], body: Data? = nil, duration: TimeInterval) {
        let statusLevel = statusCode < 400 ? LogLevel.info : LogLevel.error
        logger.log(statusLevel, message: "API Response: \(url) - Status: \(statusCode) - Duration: \(String(format: "%.3f", duration))s")

        if let body = body, let jsonString = try? JSONSerialization.jsonObject(with: body, options: []) {
            logger.debug("Response body: \(jsonString)")
        }
    }

    func logNetworkError(url: String, error: Error) {
        logger.error("API Error: \(url) - \(error.localizedDescription)")
    }
}

// 10. Log Aggregation and Filtering
class LogAggregator {
    private var loggers: [CustomLogger] = []
    private let fileLogger: FileLogger

    init() {
        fileLogger = FileLogger(filename: "aggregated.log")!
    }

    func addLogger(_ logger: CustomLogger) {
        loggers.append(logger)
    }

    func broadcast(level: LogLevel, message: String) {
        for logger in loggers {
            logger.log(level, message: message)
        }
        fileLogger.log(level: level, message: message)
    }

    func getLogsByLevel(_ level: LogLevel) -> [LogEntry] {
        var filtered: [LogEntry] = []

        for logger in loggers {
            filtered.append(contentsOf: logger.getLogs().filter { $0.level == level })
        }

        return filtered
    }
}

// Main demonstration
func demonstrateLogging() {
    print("=== macOS Swift Logging Examples ===")

    // 1. Simple logging
    let simple = SimpleLogger()
    simple.demonstrateSimpleLogging()

    // 2. Custom logger
    print("\n--- Custom Logger with Levels ---")
    let logger = CustomLogger.shared
    logger.setMinimumLevel(.info)
    logger.debug("This won't be logged")
    logger.info("Application starting...")
    logger.warning("Configuration file not found, using defaults")
    logger.error("Failed to connect to database")
    logger.critical("System out of memory!")

    // 3. File logger
    print("\n--- File Logger ---")
    if let fileLogger = FileLogger(filename: "demo.log") {
        fileLogger.log(level: .info, message: "Application started")
        fileLogger.log(level: .warning, message: "This is a warning")
        fileLogger.log(level: .error, message: "An error occurred")

        print("\nLog file entries:")
        let entries = fileLogger.readLogEntries()
        entries.forEach { print($0) }
    }

    // 4. OSLog
    let osLog = OSLogLogger()
    osLog.demonstrateOSLog()

    // 5. Performance logger
    print("\n--- Performance Logger ---")
    let perfLogger = PerformanceLogger()

    let result = perfLogger.measure("calculation") {
        var sum = 0
        for i in 1...1000 { sum += i }
        return sum
    }
    print("Calculation result: \(result)")

    // 6. Error logger
    print("\n--- Error Logger ---")
    let errorLogger = ErrorLogger()
    do {
        throw NSError(domain: "Test", code: 1, userInfo: [NSLocalizedDescriptionKey: "Test error"])
    } catch {
        errorLogger.logError(error, context: ["userId": "123", "action": "login"])
    }

    // 7. Network logger
    print("\n--- Network Logger ---")
    let networkLogger = NetworkLogger()
    networkLogger.logRequest(url: "https://api.example.com/users", method: "GET")
    networkLogger.logResponse(url: "https://api.example.com/users", statusCode: 200, duration: 0.234)
    networkLogger.logNetworkError(url: "https://api.example.com/users", error: NSError(domain: "Network", code: -1009))

    // 8. Structured logger
    print("\n--- Structured Logger ---")
    let structLogger = StructuredLogger()
    structLogger.logRequest(endpoint: "/api/users", statusCode: 200, duration: 0.123)

    print("\n=== All Logging Examples Completed ===")
}

// Run demonstration
demonstrateLogging()

💻 Validación de Parámetros swift

🟡 intermediate ⭐⭐⭐

Validar parámetros de función con predicados, validadores personalizados y mensajes de error

⏱️ 30 min 🏷️ swift, macos, validation
Prerequisites: Intermediate Swift, Generics
// macOS Swift Parameter Validation Examples
// Comprehensive parameter validation with predicates and custom validators

import Foundation

// 1. Basic Validation Functions
struct Validator {

    static func validateNotEmpty(_ value: String, fieldName: String = "Value") throws {
        guard !value.isEmpty else {
            throw ValidationError.emptyInput
        }
    }

    static func validatePositive(_ value: Int, fieldName: String = "Value") throws {
        guard value > 0 else {
            throw ValidationError.outOfRange(min: 1, max: Int.max, value: value)
        }
    }

    static func validateRange(_ value: Int, min: Int, max: Int, fieldName: String = "Value") throws {
        guard value >= min && value <= max else {
            throw ValidationError.outOfRange(min: min, max: max, value: value)
        }
    }

    static func validateMinLength(_ value: String, minLength: Int, fieldName: String = "Value") throws {
        guard value.count >= minLength else {
            throw ValidationError.tooShort(length: value.count, minLength: minLength)
        }
    }

    static func validateEmail(_ email: String) throws {
        let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)

        guard emailPredicate.evaluate(with: email) else {
            throw ValidationError.invalidFormat
        }
    }

    static func validateURL(_ url: String) throws {
        guard let _ = URL(string: url), url.hasPrefix("http") else {
            throw ValidationError.invalidFormat
        }
    }

    static func validatePhoneNumber(_ phone: String) throws {
        let phoneRegex = "^[+]{0,1}[0-9]{6,14}$"
        let phonePredicate = NSPredicate(format: "SELF MATCHES %@", phoneRegex)

        guard phonePredicate.evaluate(with: phone) else {
            throw ValidationError.invalidFormat
        }
    }
}

// 2. Validation Result
enum ValidationResult {
    case valid
    case invalid(reason: String)

    var isValid: Bool {
        if case .valid = self { return true }
        return false
    }

    var reason: String? {
        if case .invalid(let reason) = self { return reason }
        return nil
    }
}

// 3. Generic Validator
struct GenericValidator<T> {
    let validations: [(T) -> ValidationResult]

    init(@ValidationBuilder validations: () -> [(T) -> ValidationResult]) {
        self.validations = validations()
    }

    func validate(_ value: T) -> ValidationResult {
        for validation in validations {
            let result = validation(value)
            if case .invalid = result {
                return result
            }
        }
        return .valid
    }
}

@resultBuilder
struct ValidationBuilder {
    static func buildBlock(_ components: [(Any) -> ValidationResult]...) -> [(Any) -> ValidationResult] {
        components.flatMap { $0 }
    }

    static func buildExpression(_ expression: (Any) -> ValidationResult) -> [(Any) -> ValidationResult] {
        [expression]
    }
}

// 4. Common Validators
extension GenericValidator {

    static func notEmpty(fieldName: String = "Value") -> GenericValidator<String> {
        GenericValidator {
            [
                { value in
                    value.isEmpty ? .invalid(reason: "\(fieldName) cannot be empty") : .valid
                }
            ]
        }
    }

    static func minLength(_ length: Int, fieldName: String = "Value") -> GenericValidator<String> {
        GenericValidator {
            [
                { value in
                    value.count < length ? .invalid(reason: "\(fieldName) must be at least \(length) characters") : .valid
                }
            ]
        }
    }

    static func maxLength(_ length: Int, fieldName: String = "Value") -> GenericValidator<String> {
        GenericValidator {
            [
                { value in
                    value.count > length ? .invalid(reason: "\(fieldName) must not exceed \(length) characters") : .valid
                }
            ]
        }
    }

    static func range(min: Int, max: Int, fieldName: String = "Value") -> GenericValidator<Int> {
        GenericValidator {
            [
                { value in
                    value < min || value > max ? .invalid(reason: "\(fieldName) must be between \(min) and \(max)") : .valid
                    }
            ]
        }
    }

    static func positive(fieldName: String = "Value") -> GenericValidator<Int> {
        GenericValidator {
            [
                { value in
                    value <= 0 ? .invalid(reason: "\(fieldName) must be positive") : .valid
                }
            ]
        }
    }

    static func email() -> GenericValidator<String> {
        GenericValidator {
            [
                { value in
                    let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
                    let predicate = NSPredicate(format: "SELF MATCHES %@", emailRegex)
                    predicate.evaluate(with: value) ? .valid : .invalid(reason: "Invalid email format")
                }
            ]
        }
    }

    static func url() -> GenericValidator<String> {
        GenericValidator {
            [
                { value in
                    guard let url = URL(string: value) else {
                        return .invalid(reason: "Invalid URL format")
                    }
                    return url.scheme == "http" || url.scheme == "https" ? .valid : .invalid(reason: "URL must start with http:// or https://")
                }
            ]
        }
    }

    static func matches(_ regex: String, fieldName: String = "Value") -> GenericValidator<String> {
        GenericValidator {
            [
                { value in
                    let predicate = NSPredicate(format: "SELF MATCHES %@", regex)
                    predicate.evaluate(with: value) ? .valid : .invalid(reason: "\(fieldName) format is invalid")
                }
            ]
        }
    }

    static func custom(_ predicate: @escaping (T) -> Bool, errorReason: String) -> GenericValidator<T> {
        GenericValidator {
            [
                { value in
                    predicate(value) ? .valid : .invalid(reason: errorReason)
                }
            ]
        }
    }
}

// 5. Combining Validators
class CombinedValidator {

    func validateUsername(_ username: String) -> ValidationResult {
        let validator = GenericValidator
            .notEmpty(fieldName: "Username")
            .minLength(3, fieldName: "Username")
            .maxLength(20, fieldName: "Username")
            .matches("^[a-zA-Z0-9_]+$", fieldName: "Username")

        return validator.validate(username)
    }

    func validatePassword(_ password: String) -> ValidationResult {
        let hasUpperCase = NSPredicate(format: "SELF MATCHES %@", ".*[A-Z]+.*")
        let hasLowerCase = NSPredicate(format: "SELF MATCHES %@", ".*[a-z]+.*")
        let hasNumber = NSPredicate(format: "SELF MATCHES %@", ".*[0-9]+.*")
        let hasSpecial = NSPredicate(format: "SELF MATCHES %@", ".*[!@#$%^&*()_+]+.*")

        let checks = [
            hasUpperCase.evaluate(with: password),
            hasLowerCase.evaluate(with: password),
            hasNumber.evaluate(with: password),
            hasSpecial.evaluate(with: password)
        ]

        if password.count < 8 {
            return .invalid(reason: "Password must be at least 8 characters")
        }

        if !checks.allSatisfy({ $0 }) {
            return .invalid(reason: "Password must contain uppercase, lowercase, number, and special character")
        }

        return .valid
    }

    func validateAge(_ age: Int) -> ValidationResult {
        let validator = GenericValidator
            .range(min: 0, max: 150, fieldName: "Age")

        return validator.validate(age)
    }

    func validateUser(name: String, email: String, age: Int) -> [String] {
        var errors: [String] = []

        switch validateUsername(name) {
        case .invalid(let reason): errors.append(reason)
        default: break
        }

        switch GenericValidator.email().validate(email) {
        case .invalid(let reason): errors.append(reason)
        default: break
        }

        switch validateAge(age) {
        case .invalid(let reason): errors.append(reason)
        default: break
        }

        return errors
    }
}

// 6. Property Wrapper Validation
@propertyWrapper
struct ValidatedString {
    private var value: String
    private let validator: (String) -> ValidationResult

    var wrappedValue: String {
        get { value }
        set {
            let result = validator(newValue)
            if case .valid = result {
                value = newValue
            } else {
                print("Validation error: \(result.reason ?? "Unknown")")
            }
        }
    }

    init(wrappedValue: String, validator: @escaping (String) -> ValidationResult) {
        self.value = wrappedValue
        self.validator = validator
    }
}

@propertyWrapper
struct ValidatedInt {
    private var value: Int
    private let validator: (Int) -> ValidationResult

    var wrappedValue: Int {
        get { value }
        set {
            let result = validator(newValue)
            if case .valid = result {
                value = newValue
            } else {
                print("Validation error: \(result.reason ?? "Unknown")")
            }
        }
    }

    init(wrappedValue: Int, validator: @escaping (Int) -> ValidationResult) {
        self.value = wrappedValue
        self.validator = validator
    }
}

// 7. User Model with Validation
struct ValidatedUser {
    @ValidatedString(validator: GenericValidator.notEmpty().minLength(3).validate)
    var username: String

    @ValidatedString(validator: GenericValidator.email().validate)
    var email: String

    @ValidatedInt(validator: GenericValidator.range(min: 18, max: 120).validate)
    var age: Int
}

// 8. Async Validation
class AsyncValidator {

    func validateUsernameAvailable(_ username: String) async throws -> Bool {
        // Simulate API call
        try await Task.sleep(nanoseconds: 500_000_000) // 0.5 seconds

        let takenUsernames = ["admin", "root", "system", "user"]
        return !takenUsernames.contains(username.lowercased())
    }

    func validateEmailExists(_ email: String) async throws -> Bool {
        // Simulate API call
        try await Task.sleep(nanoseconds: 500_000_000)

        return email.contains("@example.com")
    }

    func validateUserAsync(username: String, email: String) async -> [String] {
        var errors: [String] = []

        // Sync validations first
        switch GenericValidator.notEmpty().minLength(3).validate(username) {
        case .invalid(let reason): errors.append(reason)
        default: break
        }

        switch GenericValidator.email().validate(email) {
        case .invalid(let reason): errors.append(reason)
        default: break
        }

        if !errors.isEmpty { return errors }

        // Async validations
        async let usernameAvailable = validateUsernameAvailable(username)
        async let emailExists = validateEmailExists(email)

        do {
            let available = try await usernameAvailable
            if !available {
                errors.append("Username is already taken")
            }
        } catch {
            errors.append("Failed to check username availability")
        }

        do {
            let exists = try await emailExists
            if !exists {
                errors.append("Email not found in system")
            }
        } catch {
            errors.append("Failed to verify email")
        }

        return errors
    }
}

// 9. Validation with Context
struct ValidationErrorWithContext {
    let field: String
    let message: String
    let value: Any

    var description: String {
        return "\(field): \(message) (value: \(value))"
    }
}

class ContextValidator {

    func validateRegistration(data: [String: Any]) -> [ValidationErrorWithContext] {
        var errors: [ValidationErrorWithContext] = []

        if let username = data["username"] as? String {
            if username.isEmpty {
                errors.append(ValidationErrorWithContext(field: "username", message: "cannot be empty", value: username))
            } else if username.count < 3 {
                errors.append(ValidationErrorWithContext(field: "username", message: "must be at least 3 characters", value: username))
            }
        } else {
            errors.append(ValidationErrorWithContext(field: "username", message: "is required", value: "nil"))
        }

        if let email = data["email"] as? String {
            let result = GenericValidator.email().validate(email)
            if case .invalid(let reason) = result {
                errors.append(ValidationErrorWithContext(field: "email", message: reason, value: email))
            }
        }

        if let age = data["age"] as? Int {
            let result = GenericValidator.range(min: 18, max: 120, fieldName: "age").validate(age)
            if case .invalid(let reason) = result {
                errors.append(ValidationErrorWithContext(field: "age", message: reason, value: age))
            }
        }

        return errors
    }
}

// 10. Predefined Validation Rules
struct ValidationRule {
    let name: String
    let validate: (Any) -> ValidationResult

    static let notEmpty = ValidationRule(name: "notEmpty") { value in
        guard let str = value as? String else {
            return .invalid(reason: "Value must be a string")
        }
        return str.isEmpty ? .invalid(reason: "cannot be empty") : .valid
    }

    static let positiveNumber = ValidationRule(name: "positiveNumber") { value in
        guard let num = value as? Int else {
            return .invalid(reason: "Value must be a number")
        }
        return num > 0 ? .valid : .invalid(reason: "must be positive")
    }

    static let emailFormat = ValidationRule(name: "emailFormat") { value in
        guard let email = value as? String else {
            return .invalid(reason: "Value must be a string")
        }
        return GenericValidator.email().validate(email)
    }
}

class RuleBasedValidator {
    func validate(value: Any, against rules: [ValidationRule]) -> [ValidationResult] {
        return rules.map { $0.validate(value) }.filter { !$0.isValid }
    }
}

// Main demonstration
func demonstrateParameterValidation() {
    print("=== macOS Swift Parameter Validation Examples ===")

    // 1. Basic validators
    print("\n--- Basic Validators ---")
    do {
        try Validator.validateNotEmpty("test")
        print("Not empty: valid")
    } catch {
        print("Error: \(error)")
    }

    do {
        try Validator.validateEmail("[email protected]")
        print("Email: valid")
    } catch {
        print("Error: \(error)")
    }

    do {
        try Validator.validateRange(15, min: 10, max: 20)
        print("Range: valid")
    } catch {
        print("Error: \(error)")
    }

    // 2. Generic validators
    print("\n--- Generic Validators ---")
    let usernameValidator = GenericValidator.notEmpty().minLength(3).maxLength(20)
    print("Username 'ab': \(usernameValidator.validate("ab"))")
    print("Username 'john': \(usernameValidator.validate("john"))")

    let ageValidator = GenericValidator.range(min: 18, max: 120)
    print("Age 15: \(ageValidator.validate(15))")
    print("Age 25: \(ageValidator.validate(25))")

    // 3. Combined validation
    print("\n--- Combined Validator ---")
    let combined = CombinedValidator()

    let userErrors = combined.validateUser(name: "jo", email: "invalid-email", age: 15)
    print("User validation errors:")
    userErrors.forEach { print("  - \($0)") }

    // 4. Context validation
    print("\n--- Context Validation ---")
    let contextValidator = ContextValidator()

    let registrationData = [
        "username": "ab",
        "email": "not-an-email",
        "age": 15
    ] as [String: Any]

    let contextErrors = contextValidator.validateRegistration(data: registrationData)
    print("Registration errors:")
    contextErrors.forEach { print("  - \($0.description)") }

    // 5. Password validation
    print("\n--- Password Validation ---")
    let combinedValidator = CombinedValidator()

    print("Password 'abc': \(combinedValidator.validatePassword("abc"))")
    print("Password 'abc123': \(combinedValidator.validatePassword("abc123"))")
    print("Password 'Abc123!@': \(combinedValidator.validatePassword("Abc123!@"))")

    // 6. Rule-based validation
    print("\n--- Rule-Based Validation ---")
    let ruleValidator = RuleBasedValidator()
    let results = ruleValidator.validate(value: -5, against: [.positiveNumber, .notEmpty])
    print("Validation results for -5:")
    results.forEach { print("  - \($0)") }

    // 7. Property wrappers
    print("\n--- Property Wrapper Validation ---")
    var user = ValidatedUser(username: "john_doe", email: "[email protected]", age: 25)
    print("Initial user: \(user.username), \(user.email), \(user.age)")

    user.username = "ja"  // Will print validation error
    user.email = "invalid"  // Will print validation error

    print("\n=== All Parameter Validation Examples Completed ===")
}

// Run demonstration
demonstrateParameterValidation()