🎯 Рекомендуемые коллекции
Балансированные коллекции примеров кода из различных категорий, которые вы можете исследовать
Примеры Обработки Ошибок macOS Swift
Примеры обработки ошибок macOS Swift включая перехват исключений, журналирование и проверку параметров
💻 Перехват Исключений swift
🟢 simple
⭐⭐
Обработка ошибок с использованием блоков do-catch, шаблонов try-catch и пользовательских типов ошибок
⏱️ 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()
💻 Журналирование swift
🟡 intermediate
⭐⭐⭐
Реализация комплексной системы журналирования с выводом в файл, уровнями и ротацией
⏱️ 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()
💻 Проверка Параметров swift
🟡 intermediate
⭐⭐⭐
Проверка параметров функции с использованием предикатов, пользовательских валидаторов и сообщений об ошибках
⏱️ 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()