macOS Error Handling Objective-C Samples

macOS Objective-C error handling examples including exception handling, logging, and parameter validation

💻 Log Recording objectivec

🟢 simple ⭐⭐

Write logs to files with different levels and timestamps using NSLog and custom loggers

⏱️ 20 min 🏷️ objectivec, macos, error handling, logging
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C Log Recording Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Log Levels

typedef NS_ENUM(NSInteger, LogLevel) {
    LogLevelDebug = 0,
    LogLevelInfo = 1,
    LogLevelWarning = 2,
    LogLevelError = 3,
    LogLevelCritical = 4
};

NSString *NSStringFromLogLevel(LogLevel level) {
    switch (level) {
        case LogLevelDebug:    return @"DEBUG";
        case LogLevelInfo:     return @"INFO";
        case LogLevelWarning:  return @"WARNING";
        case LogLevelError:    return @"ERROR";
        case LogLevelCritical: return @"CRITICAL";
        default:               return @"UNKNOWN";
    }
}

// MARK: - 2. Simple File Logger

@interface SimpleFileLogger : NSObject

@property (nonatomic, strong) NSString *logFilePath;
@property (nonatomic, assign) LogLevel minimumLevel;

+ (instancetype)loggerWithFile:(NSString *)filePath;
- (void)log:(LogLevel)level message:(NSString *)message;
- (void)debug:(NSString *)message;
- (void)info:(NSString *)message;
- (void)warning:(NSString *)message;
- (void)error:(NSString *)message;
- (void)critical:(NSString *)message;

@end

@implementation SimpleFileLogger

+ (instancetype)loggerWithFile:(NSString *)filePath {
    SimpleFileLogger *logger = [[SimpleFileLogger alloc] init];
    logger.logFilePath = filePath;
    logger.minimumLevel = LogLevelDebug;
    return logger;
}

- (void)log:(LogLevel)level message:(NSString *)message {
    if (level < self.minimumLevel) {
        return;
    }

    // Create log entry
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
    NSString *timestamp = [formatter stringFromDate:[NSDate date]];

    NSString *logEntry = [NSString stringWithFormat:@"[%@] [%@] %@\n",
                         timestamp, NSStringFromLogLevel(level), message];

    // Append to file
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.logFilePath];

    if (!fileHandle) {
        // Create new file
        [[NSFileManager defaultManager] createFileAtPath:self.logFilePath
                                                contents:nil
                                              attributes:nil];
        fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.logFilePath];
    }

    if (fileHandle) {
        [fileHandle seekToEndOfFile];
        [fileHandle writeData:[logEntry dataUsingEncoding:NSUTF8StringEncoding]];
        [fileHandle closeFile];
    }

    // Also print to console
    NSLog(@"[%@] %@", NSStringFromLogLevel(level), message);
}

- (void)debug:(NSString *)message {
    [self log:LogLevelDebug message:message];
}

- (void)info:(NSString *)message {
    [self log:LogLevelInfo message:message];
}

- (void)warning:(NSString *)message {
    [self log:LogLevelWarning message:message];
}

- (void)error:(NSString *)message {
    [self log:LogLevelError message:message];
}

- (void)critical:(NSString *)message {
    [self log:LogLevelCritical message:message];
}

@end

// MARK: - 3. Advanced Logger with Rotation

@interface AdvancedLogger : NSObject

@property (nonatomic, strong) NSString *logDirectory;
@property (nonatomic, strong) NSString *currentLogFile;
@property (nonatomic, assign) NSUInteger maxFileSize;
@property (nonatomic, assign) NSUInteger maxBackupFiles;

+ (instancetype)loggerWithDirectory:(NSString *)directory
                        maxFileSize:(NSUInteger)maxFileSize
                     maxBackupFiles:(NSUInteger)maxBackupFiles;

- (void)log:(LogLevel)level
     file:(const char *)file
     line:(NSInteger)line
   method:(const char *)method
   message:(NSString *)message;

- (void)rotateLogFile;

@end

@implementation AdvancedLogger

+ (instancetype)loggerWithDirectory:(NSString *)directory
                        maxFileSize:(NSUInteger)maxFileSize
                     maxBackupFiles:(NSUInteger)maxBackupFiles {

    AdvancedLogger *logger = [[AdvancedLogger alloc] init];
    logger.logDirectory = directory;
    logger.maxFileSize = maxFileSize;
    logger.maxBackupFiles = maxBackupFiles;

    // Create directory if needed
    [[NSFileManager defaultManager] createDirectoryAtPath:directory
                             withIntermediateDirectories:YES
                                              attributes:nil
                                                   error:nil];

    // Set current log file
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.dateFormat = @"yyyy-MM-dd";
    NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];
    logger.currentLogFile = [directory stringByAppendingPathComponent:
                            [NSString stringWithFormat:@"app_%@.log", dateString]];

    return logger;
}

- (void)log:(LogLevel)level
     file:(const char *)file
     line:(NSInteger)line
   method:(const char *)method
   message:(NSString *)message {

    // Check file size and rotate if needed
    NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.currentLogFile
                                                                                error:nil];
    if (attributes) {
        NSUInteger fileSize = [attributes[NSFileSize] unsignedIntegerValue];
        if (fileSize >= self.maxFileSize) {
            [self rotateLogFile];
        }
    }

    // Create log entry
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
    NSString *timestamp = [formatter stringFromDate:[NSDate date]];

    NSString *fileName = [[NSString stringWithUTF8String:file] lastPathComponent];
    NSString *methodName = [NSString stringWithUTF8String:method];

    NSString *logEntry = [NSString stringWithFormat:@"[%@] [%@] [%@:%@:%ld] %@\n",
                         timestamp,
                         NSStringFromLogLevel(level),
                         fileName,
                         methodName,
                         (long)line,
                         message];

    // Write to file
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.currentLogFile];

    if (!fileHandle) {
        [[NSFileManager defaultManager] createFileAtPath:self.currentLogFile
                                                contents:nil
                                              attributes:nil];
        fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.currentLogFile];
    }

    if (fileHandle) {
        [fileHandle seekToEndOfFile];
        [fileHandle writeData:[logEntry dataUsingEncoding:NSUTF8StringEncoding]];
        [fileHandle closeFile];
    }
}

- (void)rotateLogFile {
    NSString *baseName = [[self.currentLogFile lastPathComponent] stringByDeletingPathExtension];
    NSString *extension = [self.currentLogFile pathExtension];

    // Remove oldest backup if needed
    NSString *oldestBackup = [self.logDirectory stringByAppendingPathComponent:
                             [NSString stringWithFormat:@"%@_%ld.%@",
                              baseName, (long)(self.maxBackupFiles + 1), extension]];
    [[NSFileManager defaultManager] removeItemAtPath:oldestBackup error:nil];

    // Rotate existing backups
    for (NSInteger i = self.maxBackupFiles; i >= 1; i--) {
        NSString *oldFile = [self.logDirectory stringByAppendingPathComponent:
                            [NSString stringWithFormat:@"%@_%ld.%@", baseName, (long)i, extension]];
        NSString *newFile = [self.logDirectory stringByAppendingPathComponent:
                            [NSString stringWithFormat:@"%@_%ld.%@", baseName, (long)(i + 1), extension]];

        [[NSFileManager defaultManager] moveItemAtPath:oldFile toPath:newFile error:nil];
    }

    // Move current log to backup_1
    NSString *backup1 = [self.logDirectory stringByAppendingPathComponent:
                        [NSString stringWithFormat:@"%@_1.%@", baseName, extension]];
    [[NSFileManager defaultManager] moveItemAtPath:self.currentLogFile toPath:backup1 error:nil];

    NSLog(@"Log file rotated: %@", self.currentLogFile);
}

@end

// MARK: - 4. Structured Logging

@interface StructuredLogger : NSObject

@property (nonatomic, strong) NSString *logFilePath;

- (void)logWithLevel:(LogLevel)level
             message:(NSString *)message
              fields:(NSDictionary *)fields;

- (void)logEvent:(NSString *)eventName
          fields:(NSDictionary *)fields;

- (void)logError:(NSError *)error
          context:(NSDictionary *)context;

@end

@implementation StructuredLogger

- (instancetype)init {
    self = [super init];

    if (self) {
        _logFilePath = @"/tmp/structured_log.jsonl";
    }

    return self;
}

- (void)logWithLevel:(LogLevel)level
             message:(NSString *)message
              fields:(NSDictionary *)fields {

    NSMutableDictionary *logEntry = [NSMutableDictionary dictionary];
    logEntry[@"timestamp"] = @([NSDate date].timeIntervalSince1970);
    logEntry[@"level"] = NSStringFromLogLevel(level);
    logEntry[@"message"] = message;

    if (fields) {
        [logEntry addEntriesFromDictionary:fields];
    }

    [self writeLogEntry:logEntry];
}

- (void)logEvent:(NSString *)eventName
          fields:(NSDictionary *)fields {

    [self logWithLevel:LogLevelInfo
              message:[NSString stringWithFormat:@"Event: %@", eventName]
               fields:fields];
}

- (void)logError:(NSError *)error
          context:(NSDictionary *)context {

    NSMutableDictionary *errorFields = [NSMutableDictionary dictionary];
    errorFields[@"errorDomain"] = error.domain;
    errorFields[@"errorCode"] = @(error.code);
    errorFields[@"errorDescription"] = error.localizedDescription;

    if (context) {
        [errorFields addEntriesFromDictionary:context];
    }

    [self logWithLevel:LogLevelError
              message:@"Error occurred"
               fields:errorFields];
}

- (void)writeLogEntry:(NSDictionary *)logEntry {
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:logEntry
                                                       options:0
                                                         error:nil];

    if (jsonData) {
        NSString *jsonLine = [NSString stringWithFormat:@"%@\n",
                             [[NSString alloc] initWithData:jsonData
                                                   encoding:NSUTF8StringEncoding]];

        NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.logFilePath];

        if (!fileHandle) {
            [[NSFileManager defaultManager] createFileAtPath:self.logFilePath
                                                    contents:nil
                                                  attributes:nil];
            fileHandle = [NSFileHandle fileHandleForWritingAtPath:self.logFilePath];
        }

        if (fileHandle) {
            [fileHandle seekToEndOfFile];
            [fileHandle writeData:[jsonLine dataUsingEncoding:NSUTF8StringEncoding]];
            [fileHandle closeFile];
        }
    }
}

@end

// MARK: - 5. Performance Logging

@interface PerformanceLogger : NSObject

@property (nonatomic, strong) NSMutableDictionary *startTime;

- (void)startTiming:(NSString *)operationName;
- (NSTimeInterval)endTiming:(NSString *)operationName;
- (void)logTiming:(NSString *)operationName duration:(NSTimeInterval)duration;

@end

@implementation PerformanceLogger

- (instancetype)init {
    self = [super init];

    if (self) {
        _startTime = [NSMutableDictionary dictionary];
    }

    return self;
}

- (void)startTiming:(NSString *)operationName {
    self.startTime[operationName] = @([NSDate date].timeIntervalSince1970);
}

- (NSTimeInterval)endTiming:(NSString *)operationName {
    NSNumber *start = self.startTime[operationName];

    if (!start) {
        NSLog(@"No start time found for operation: %@", operationName);
        return 0;
    }

    NSTimeInterval end = [NSDate date].timeIntervalSince1970;
    NSTimeInterval duration = end - [start doubleValue];

    [self.startTime removeObjectForKey:operationName];
    [self logTiming:operationName duration:duration];

    return duration;
}

- (void)logTiming:(NSString *)operationName duration:(NSTimeInterval)duration {
    NSString *logMessage = [NSString stringWithFormat:@"Operation '%@' completed in %.3f seconds",
                           operationName, duration];

    NSLog(@"[PERFORMANCE] %@", logMessage);

    // Also write to performance log file
    NSString *perfLogFile = @"/tmp/performance.log";
    NSString *logEntry = [NSString stringWithFormat:@"[%@] %@\n",
                         [NSDate date], logEntry];

    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:perfLogFile];

    if (!fileHandle) {
        [[NSFileManager defaultManager] createFileAtPath:perfLogFile
                                                contents:nil
                                              attributes:nil];
        fileHandle = [NSFileHandle fileHandleForWritingAtPath:perfLogFile];
    }

    if (fileHandle) {
        [fileHandle seekToEndOfFile];
        [fileHandle writeData:[logEntry dataUsingEncoding:NSUTF8StringEncoding]];
        [fileHandle closeFile];
    }
}

@end

// MARK: - Main Demonstration

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"=== macOS Objective-C Log Recording Examples ===\n");

        // 1. Simple file logging
        NSLog(@"--- 1. Simple File Logging ---");

        SimpleFileLogger *logger = [SimpleFileLogger loggerWithFile:@"/tmp/app.log"];

        [logger debug:@"Application starting"];
        [logger info:@"User logged in"];
        [logger warning:@"Low memory warning"];
        [logger error:@"Database connection failed"];
        [logger critical:@"System shutdown required"];

        NSLog(@"\nLogs written to /tmp/app.log");

        // 2. Advanced logging
        NSLog(@"\n--- 2. Advanced Logging ---");

        AdvancedLogger *advLogger = [AdvancedLogger loggerWithDirectory:@"/tmp/logs"
                                                             maxFileSize:1024
                                                          maxBackupFiles:3];

        [advLogger log:LogLevelInfo
                   file:__FILE__
                   line:__LINE__
                 method:__func__
                 message:@"Advanced logger initialized"];

        [advLogger log:LogLevelError
                   file:__FILE__
                   line:__LINE__
                 method:__func__
                 message:@"Test error message"];

        NSLog(@"\nAdvanced logs written to /tmp/logs/");

        // 3. Structured logging
        NSLog(@"\n--- 3. Structured Logging ---");

        StructuredLogger *structLogger = [[StructuredLogger alloc] init];

        [structLogger logWithLevel:LogLevelInfo
                          message:@"User action"
                           fields:@{
                               @"userId": @"12345",
                               @"action": @"login",
                               @"ipAddress": @"192.168.1.1"
                           }];

        [structLogger logEvent:@"purchase_completed"
                      fields:@{
                          @"userId": @"12345",
                          @"productId": @"prod-678",
                          @"amount": @99.99
                      }];

        NSError *testError = [NSError errorWithDomain:@"TestDomain"
                                               code:404
                                           userInfo:@{
                                               NSLocalizedDescriptionKey: @"Resource not found"
                                           }];

        [structLogger logError:testError
                      context:@{
                          @"requestId": @"req-123",
                          @"endpoint": @"/api/users"
                      }];

        NSLog(@"\nStructured logs written to /tmp/structured_log.jsonl");

        // 4. Performance logging
        NSLog(@"\n--- 4. Performance Logging ---");

        PerformanceLogger *perfLogger = [[PerformanceLogger alloc] init];

        [perfLogger startTiming:@"dataProcessing"];
        [NSThread sleepForTimeInterval:0.1]; // Simulate work
        [perfLogger endTiming:@"dataProcessing"];

        [perfLogger startTiming:@"databaseQuery"];
        [NSThread sleepForTimeInterval:0.05]; // Simulate query
        [perfLogger endTiming:@"databaseQuery"];

        // 5. Log level filtering
        NSLog(@"\n--- 5. Log Level Filtering ---");

        SimpleFileLogger *warningLogger = [SimpleFileLogger loggerWithFile:@"/tmp/warning.log"];
        warningLogger.minimumLevel = LogLevelWarning;

        [warningLogger debug:@"This won't be logged"];
        [warningLogger info:@"Neither will this"];
        [warningLogger warning:@"This warning will be logged"];
        [warningLogger error:@"This error will be logged"];

        NSLog(@"\nFiltered logs written to /tmp/warning.log");

        // Cleanup
        NSLog(@"\n--- Cleanup ---");

        NSArray *logFiles = @[
            @"/tmp/app.log",
            @"/tmp/structured_log.jsonl",
            @"/tmp/warning.log",
            @"/tmp/performance.log"
        ];

        for (NSString *file in logFiles) {
            NSString *content = [NSString stringWithContentsOfFile:file
                                                         encoding:NSUTF8StringEncoding
                                                            error:nil];
            if (content) {
                NSLog(@"\nContent of %@:", file);
                NSLog(@"%@", content);
            }
        }

        // Clean up log directory
        [[NSFileManager defaultManager] removeItemAtPath:@"/tmp/logs" error:nil];

        NSLog(@"\n=== Log Recording Examples Completed ===");
    }

    return 0;
}

💻 Parameter Validation objectivec

🟢 simple ⭐⭐

Validate function parameters including nil checks, range validation, and type checking

⏱️ 20 min 🏷️ objectivec, macos, error handling, validation
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C Parameter Validation Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Basic Validation Macros

#define VALIDATE_NOT_NULL(param, error) \
    if (!param) { \
        if (error) { \
            *error = [NSError errorWithDomain:@"ValidationError" \
                                         code:1001 \
                                     userInfo:@{NSLocalizedDescriptionKey: @"Parameter cannot be null"}]; \
        } \
        return NO; \
    }

#define VALIDATE_NOT_EMPTY_STRING(param, error) \
    if (!param || param.length == 0) { \
        if (error) { \
            *error = [NSError errorWithDomain:@"ValidationError" \
                                         code:1002 \
                                     userInfo:@{NSLocalizedDescriptionKey: @"String cannot be empty"}]; \
        } \
        return NO; \
    }

#define VALIDATE_RANGE(param, min, max, error) \
    if (param < min || param > max) { \
        if (error) { \
            *error = [NSError errorWithDomain:@"ValidationError" \
                                         code:1003 \
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Parameter must be between %ld and %ld", (long)(min), (long)(max)]}]; \
        } \
        return NO; \
    }

// MARK: - 2. String Validation

@interface StringValidator : NSObject

+ (BOOL)isValidString:(NSString *)string
                error:(NSError **)error;

+ (BOOL)isValidEmail:(NSString *)email
               error:(NSError **)error;

+ (BOOL)isValidURL:(NSString *)url
             error:(NSError **)error;

+ (BOOL)isValidPhoneNumber:(NSString *)phone
                     error:(NSError **)error;

+ (BOOL)isValidLength:(NSString *)string
            minLength:(NSInteger)minLength
            maxLength:(NSInteger)maxLength
                 error:(NSError **)error;

@end

@implementation StringValidator

+ (BOOL)isValidString:(NSString *)string
                error:(NSError **)error {

    if (!string) {
        if (error) {
            *error = [NSError errorWithDomain:@"StringValidationError"
                                         code:1001
                                     userInfo:@{NSLocalizedDescriptionKey: @"String cannot be nil"}];
        }
        return NO;
    }

    if (string.length == 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"StringValidationError"
                                         code:1002
                                     userInfo:@{NSLocalizedDescriptionKey: @"String cannot be empty"}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)isValidEmail:(NSString *)email
               error:(NSError **)error {

    if (![self isValidString:email error:error]) {
        return NO;
    }

    NSString *emailRegex = @"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}";
    NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", emailRegex];

    if (![emailTest evaluateWithObject:email]) {
        if (error) {
            *error = [NSError errorWithDomain:@"StringValidationError"
                                         code:1003
                                     userInfo:@{NSLocalizedDescriptionKey: @"Invalid email format"}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)isValidURL:(NSString *)url
             error:(NSError **)error {

    if (![self isValidString:url error:error]) {
        return NO;
    }

    NSURL *urlObj = [NSURL URLWithString:url];

    if (!urlObj || !urlObj.scheme || !urlObj.host) {
        if (error) {
            *error = [NSError errorWithDomain:@"StringValidationError"
                                         code:1004
                                     userInfo:@{NSLocalizedDescriptionKey: @"Invalid URL format"}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)isValidPhoneNumber:(NSString *)phone
                     error:(NSError **)error {

    if (![self isValidString:phone error:error]) {
        return NO;
    }

    // Basic phone validation: digits, spaces, dashes, parentheses, plus
    NSString *phoneRegex = @"^[+]?[(]?[0-9]{1,3}[)]?[-\\s.]?[(]?[0-9]{1,4}[)]?[-\\s.]?[0-9]{1,4}[-\\s.]?[0-9]{1,9}$";
    NSPredicate *phoneTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", phoneRegex];

    if (![phoneTest evaluateWithObject:phone]) {
        if (error) {
            *error = [NSError errorWithDomain:@"StringValidationError"
                                         code:1005
                                     userInfo:@{NSLocalizedDescriptionKey: @"Invalid phone number format"}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)isValidLength:(NSString *)string
            minLength:(NSInteger)minLength
            maxLength:(NSInteger)maxLength
                 error:(NSError **)error {

    if (![self isValidString:string error:error]) {
        return NO;
    }

    if (string.length < minLength || string.length > maxLength) {
        if (error) {
            *error = [NSError errorWithDomain:@"StringValidationError"
                                         code:1006
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"String length must be between %ld and %ld", (long)minLength, (long)maxLength]}];
        }
        return NO;
    }

    return YES;
}

@end

// MARK: - 3. Numeric Validation

@interface NumericValidator : NSObject

+ (BOOL)isValidInteger:(NSInteger)value
                min:(NSInteger)min
                max:(NSInteger)max
               error:(NSError **)error;

+ (BOOL)isValidDouble:(double)value
                  min:(double)min
                  max:(double)max
                 error:(NSError **)error;

+ (BOOL)isPositive:(NSInteger)value
              error:(NSError **)error;

+ (BOOL)isNonNegative:(double)value
                 error:(NSError **)error;

@end

@implementation NumericValidator

+ (BOOL)isValidInteger:(NSInteger)value
                   min:(NSInteger)min
                   max:(NSInteger)max
                  error:(NSError **)error {

    if (value < min || value > max) {
        if (error) {
            *error = [NSError errorWithDomain:@"NumericValidationError"
                                         code:2001
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Value %ld must be between %ld and %ld", (long)value, (long)min, (long)max]}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)isValidDouble:(double)value
                  min:(double)min
                  max:(double)max
                 error:(NSError **)error {

    if (value < min || value > max) {
        if (error) {
            *error = [NSError errorWithDomain:@"NumericValidationError"
                                         code:2002
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Value %.2f must be between %.2f and %.2f", value, min, max]}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)isPositive:(NSInteger)value
              error:(NSError **)error {

    if (value <= 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"NumericValidationError"
                                         code:2003
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Value %ld must be positive", (long)value]}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)isNonNegative:(double)value
                 error:(NSError **)error {

    if (value < 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"NumericValidationError"
                                         code:2004
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Value %.2f cannot be negative", value]}];
        }
        return NO;
    }

    return YES;
}

@end

// MARK: - 4. Collection Validation

@interface CollectionValidator : NSObject

+ (BOOL)isValidArray:(NSArray *)array
        minCount:(NSInteger)minCount
        maxCount:(NSInteger)maxCount
            error:(NSError **)error;

+ (BOOL)arrayContains:(NSArray *)array
                   type:(Class)classType
                  error:(NSError **)error;

+ (BOOL)isValidDictionary:(NSDictionary *)dict
               requiredKeys:(NSArray<NSString *> *)requiredKeys
                    error:(NSError **)error;

@end

@implementation CollectionValidator

+ (BOOL)isValidArray:(NSArray *)array
        minCount:(NSInteger)minCount
        maxCount:(NSInteger)maxCount
            error:(NSError **)error {

    if (!array) {
        if (error) {
            *error = [NSError errorWithDomain:@"CollectionValidationError"
                                         code:3001
                                     userInfo:@{NSLocalizedDescriptionKey: @"Array cannot be nil"}];
        }
        return NO;
    }

    if (array.count < minCount || array.count > maxCount) {
        if (error) {
            *error = [NSError errorWithDomain:@"CollectionValidationError"
                                         code:3002
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Array count %lu must be between %ld and %ld", (unsigned long)array.count, (long)minCount, (long)maxCount]}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)arrayContains:(NSArray *)array
                   type:(Class)classType
                  error:(NSError **)error {

    if (!array) {
        if (error) {
            *error = [NSError errorWithDomain:@"CollectionValidationError"
                                         code:3003
                                     userInfo:@{NSLocalizedDescriptionKey: @"Array cannot be nil"}];
        }
        return NO;
    }

    for (id item in array) {
        if (![item isKindOfClass:classType]) {
            if (error) {
                *error = [NSError errorWithDomain:@"CollectionValidationError"
                                             code:3004
                                         userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Array must contain only %@ objects", NSStringFromClass(classType)]}];
            }
            return NO;
        }
    }

    return YES;
}

+ (BOOL)isValidDictionary:(NSDictionary *)dict
               requiredKeys:(NSArray<NSString *> *)requiredKeys
                    error:(NSError **)error {

    if (!dict) {
        if (error) {
            *error = [NSError errorWithDomain:@"CollectionValidationError"
                                         code:3005
                                     userInfo:@{NSLocalizedDescriptionKey: @"Dictionary cannot be nil"}];
        }
        return NO;
    }

    NSMutableSet *missingKeys = [NSMutableSet set];

    for (NSString *key in requiredKeys) {
        if (!dict[key]) {
            [missingKeys addObject:key];
        }
    }

    if (missingKeys.count > 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"CollectionValidationError"
                                         code:3006
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Missing required keys: %@", [missingKeys allObjects]}]};
        }
        return NO;
    }

    return YES;
}

@end

// MARK: - 5. User Input Validator

@interface UserInputValidator : NSObject

+ (BOOL)validateUsername:(NSString *)username
                   error:(NSError **)error;

+ (BOOL)validatePassword:(NSString *)password
                   error:(NSError **)error;

+ (BOOL)validateAge:(NSInteger)age
              error:(NSError **)error;

+ (BOOL)validateUserDictionary:(NSDictionary *)userDict
                          error:(NSError **)error;

@end

@implementation UserInputValidator

+ (BOOL)validateUsername:(NSString *)username
                   error:(NSError **)error {

    if (![StringValidator isValidString:username error:error]) {
        return NO;
    }

    // Username: 3-20 characters, alphanumeric and underscore
    NSString *usernameRegex = @"^[a-zA-Z0-9_]{3,20}$";
    NSPredicate *usernameTest = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", usernameRegex];

    if (![usernameTest evaluateWithObject:username]) {
        if (error) {
            *error = [NSError errorWithDomain:@"UserValidationError"
                                         code:4001
                                     userInfo:@{NSLocalizedDescriptionKey: @"Username must be 3-20 alphanumeric characters or underscores"}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)validatePassword:(NSString *)password
                   error:(NSError **)error {

    if (![StringValidator isValidString:password error:error]) {
        return NO;
    }

    // Password: at least 8 characters, must contain uppercase, lowercase, and digit
    if (password.length < 8) {
        if (error) {
            *error = [NSError errorWithDomain:@"UserValidationError"
                                         code:4002
                                     userInfo:@{NSLocalizedDescriptionKey: @"Password must be at least 8 characters"}];
        }
        return NO;
    }

    BOOL hasUppercase = NO, hasLowercase = NO, hasDigit = NO;

    for (NSInteger i = 0; i < password.length; i++) {
        unichar c = [password characterAtIndex:i];

        if (c >= 'A' && c <= 'Z') hasUppercase = YES;
        else if (c >= 'a' && c <= 'z') hasLowercase = YES;
        else if (c >= '0' && c <= '9') hasDigit = YES;
    }

    if (!hasUppercase || !hasLowercase || !hasDigit) {
        if (error) {
            *error = [NSError errorWithDomain:@"UserValidationError"
                                         code:4003
                                     userInfo:@{NSLocalizedDescriptionKey: @"Password must contain uppercase, lowercase, and digit"}];
        }
        return NO;
    }

    return YES;
}

+ (BOOL)validateAge:(NSInteger)age
              error:(NSError **)error {

    return [NumericValidator isValidInteger:age min:13 max:120 error:error];
}

+ (BOOL)validateUserDictionary:(NSDictionary *)userDict
                          error:(NSError **)error {

    // Check required fields
    NSArray *requiredKeys = @[@"username", @"password", @"age", @"email"];

    if (![CollectionValidator isValidDictionary:userDict
                                    requiredKeys:requiredKeys
                                         error:error]) {
        return NO;
    }

    // Validate each field
    NSString *username = userDict[@"username"];
    if (![self validateUsername:username error:error]) {
        return NO;
    }

    NSString *password = userDict[@"password"];
    if (![self validatePassword:password error:error]) {
        return NO;
    }

    NSNumber *ageNum = userDict[@"age"];
    if (![self validateAge:ageNum.integerValue error:error]) {
        return NO;
    }

    NSString *email = userDict[@"email"];
    if (![StringValidator isValidEmail:email error:error]) {
        return NO;
    }

    return YES;
}

@end

// MARK: - Main Demonstration

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"=== macOS Objective-C Parameter Validation Examples ===\n");

        NSError *error = nil;

        // 1. String validation
        NSLog(@"--- 1. String Validation ---");

        BOOL valid = [StringValidator isValidString:@"test" error:&error];
        NSLog(@"Valid string: %@", valid ? @"YES" : @"NO");

        valid = [StringValidator isValidString:@"" error:&error];
        NSLog(@"Empty string valid: %@", valid ? @"YES" : @"NO");
        if (!valid) NSLog(@"  Error: %@", error.localizedDescription);

        valid = [StringValidator isValidEmail:@"[email protected]" error:&error];
        NSLog(@"Valid email: %@", valid ? @"YES" : @"NO");

        valid = [StringValidator isValidEmail:@"invalid" error:&error];
        NSLog(@"Invalid email: %@", valid ? @"YES" : @"NO");
        if (!valid) NSLog(@"  Error: %@", error.localizedDescription);

        valid = [StringValidator isValidURL:@"https://example.com" error:&error];
        NSLog(@"Valid URL: %@", valid ? @"YES" : @"NO");

        // 2. Numeric validation
        NSLog(@"\n--- 2. Numeric Validation ---");

        valid = [NumericValidator isValidInteger:50 min:1 max:100 error:&error];
        NSLog(@"50 in range [1,100]: %@", valid ? @"YES" : @"NO");

        valid = [NumericValidator isValidInteger:150 min:1 max:100 error:&error];
        NSLog(@"150 in range [1,100]: %@", valid ? @"YES" : @"NO");
        if (!valid) NSLog(@"  Error: %@", error.localizedDescription);

        valid = [NumericValidator isPositive:10 error:&error];
        NSLog(@"10 is positive: %@", valid ? @"YES" : @"NO");

        valid = [NumericValidator isNonNegative:-5 error:&error];
        NSLog(@"-5 is non-negative: %@", valid ? @"YES" : @"NO");

        // 3. Collection validation
        NSLog(@"\n--- 3. Collection Validation ---");

        NSArray *array = @[@1, @2, @3];
        valid = [CollectionValidator isValidArray:array minCount:1 maxCount:5 error:&error];
        NSLog(@"Array with 3 items [1,5]: %@", valid ? @"YES" : @"NO");

        valid = [CollectionValidator arrayContains:array type:[NSNumber class] error:&error];
        NSLog(@"Array contains only numbers: %@", valid ? @"YES" : @"NO");

        NSDictionary *dict = @{@"name": @"John", @"age": @30};
        NSArray *requiredKeys = @[@"name", @"age"];
        valid = [CollectionValidator isValidDictionary:dict requiredKeys:requiredKeys error:&error];
        NSLog(@"Dictionary has required keys: %@", valid ? @"YES" : @"NO");

        // 4. User input validation
        NSLog(@"\n--- 4. User Input Validation ---");

        valid = [UserInputValidator validateUsername:@"john_doe" error:&error];
        NSLog(@"Username 'john_doe' valid: %@", valid ? @"YES" : @"NO");

        valid = [UserInputValidator validatePassword:@"Pass123" error:&error];
        NSLog(@"Password 'Pass123' valid: %@", valid ? @"YES" : @"NO");

        valid = [UserInputValidator validateAge:25 error:&error];
        NSLog(@"Age 25 valid: %@", valid ? @"YES" : @"NO");

        // 5. Complete user validation
        NSLog(@"\n--- 5. Complete User Validation ---");

        NSDictionary *validUser = @{
            @"username": @"jane_doe",
            @"password": @"SecurePass123",
            @"age": @28,
            @"email": @"[email protected]"
        };

        valid = [UserInputValidator validateUserDictionary:validUser error:&error];
        NSLog(@"Valid user dictionary: %@", valid ? @"YES" : @"NO");

        NSDictionary *invalidUser = @{
            @"username": @"ab",
            @"password": @"weak",
            @"age": @10,
            @"email": @"not-an-email"
        };

        valid = [UserInputValidator validateUserDictionary:invalidUser error:&error];
        NSLog(@"Invalid user dictionary: %@", valid ? @"YES" : @"NO");
        if (!valid) NSLog(@"  Error: %@", error.localizedDescription);

        NSLog(@"\n=== Parameter Validation Examples Completed ===");
    }

    return 0;
}

💻 Exception Handling objectivec

🟡 intermediate ⭐⭐⭐

Handle exceptions using @try-@catch-@finally blocks and NSException

⏱️ 25 min 🏷️ objectivec, macos, error handling, exception
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C Exception Handling Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Basic Exception Handling

@interface ExceptionHandler : NSObject

+ (void)demonstrateBasicException;
+ (void)demonstrateMultipleCatchBlocks;
+ (void)demonstrateFinallyBlock;

@end

@implementation ExceptionHandler

+ (void)demonstrateBasicException {
    NSLog(@"--- Basic Exception Handling ---");

    @try {
        NSLog(@"Entering try block");

        // Throw an exception
        [NSException raise:@"TestException"
                    format:@"This is a test exception"];

        NSLog(@"This will not execute");
    }
    @catch (NSException *exception) {
        NSLog(@"Caught exception: %@", exception.name);
        NSLog(@"Reason: %@", exception.reason);
    }
    @finally {
        NSLog(@"Finally block always executes");
    }

    NSLog(@"\nContinuing after exception handling\n");
}

+ (void)demonstrateMultipleCatchBlocks {
    NSLog(@"--- Multiple Catch Blocks ---");

    @try {
        NSLog(@"Entering try block");

        // Simulate different exception types
        NSInteger exceptionType = 1; // Change to test different exceptions

        switch (exceptionType) {
            case 1:
                [NSException raise:@"InvalidArgumentException"
                            format:@"Invalid argument provided"];
                break;
            case 2:
                [NSException raise:@"RangeException"
                            format:@"Index out of bounds"];
                break;
            default:
                [NSException raise:@"GenericException"
                            format:@"Something went wrong"];
        }
    }
    @catch (NSException *exception) {
        if ([exception.name isEqualToString:@"InvalidArgumentException"]) {
            NSLog(@"Caught invalid argument: %@", exception.reason);
        } else if ([exception.name isEqualToString:@"RangeException"]) {
            NSLog(@"Caught range exception: %@", exception.reason);
        } else {
            NSLog(@"Caught generic exception: %@", exception.reason);
        }
    }
    @finally {
        NSLog(@"Cleanup in finally block");
    }

    NSLog(@"\n");
}

+ (void)demonstrateFinallyBlock {
    NSLog(@"--- Finally Block Demo ---");

    NSFileHandle *fileHandle = nil;
    BOOL success = NO;

    @try {
        NSLog(@"Opening file");

        // Simulate file operations
        NSString *testFile = @"/tmp/test_exception.txt";
        [@("Test content") writeToFile:testFile
                              atomically:YES
                                encoding:NSUTF8StringEncoding
                                   error:nil];

        fileHandle = [NSFileHandle fileHandleForReadingAtPath:testFile];
        success = YES;

        // Simulate an exception
        if (YES) {
            [NSException raise:@"FileException"
                        format:@"Error processing file"];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"Caught exception: %@", exception.reason);
        success = NO;
    }
    @finally {
        NSLog(@"Finally block: Cleaning up resources");

        if (fileHandle) {
            [fileHandle closeFile];
            NSLog(@"File handle closed");
        }

        // Cleanup test file
        [[NSFileManager defaultManager] removeItemAtPath:@"/tmp/test_exception.txt" error:nil];
    }

    NSLog(@"Operation completed %@\n", success ? @"successfully" : @"with errors");
}

@end

// MARK: - 2. Custom Exception Classes

@interface CustomValidationException : NSException

+ (instancetype)exceptionWithReason:(NSString *)reason
                          field:(NSString *)field
                            value:(id)value;

@property (nonatomic, readonly) NSString *field;
@property (nonatomic, readonly) id value;

@end

@implementation CustomValidationException

+ (instancetype)exceptionWithReason:(NSString *)reason
                          field:(NSString *)field
                            value:(id)value {

    NSString *fullReason = [NSString stringWithFormat:@"Field '%@' failed validation: %@ (value: %@)",
                           field, reason, value];

    CustomValidationException *exception = [[CustomValidationException alloc]
        initWithName:@"ValidationException"
              reason:fullReason
            userInfo:@{@"field": field, @"value": value ?: [NSNull null]}];

    return exception;
}

- (NSString *)field {
    return self.userInfo[@"field"];
}

- (id)value {
    return self.userInfo[@"value"];
}

@end

// MARK: - 3. Exception in Methods

@interface SafeCalculator : NSObject

+ (NSInteger)divide:(NSInteger)numerator
          by:(NSInteger)denominator
       error:(NSError **)error;

+ (NSInteger)safeDivide:(NSInteger)numerator
                    by:(NSInteger)denominator
                 error:(NSError **)error;

@end

@implementation SafeCalculator

+ (NSInteger)divide:(NSInteger)numerator
          by:(NSInteger)denominator
       error:(NSError **)error {

    if (denominator == 0) {
        [NSException raise:@"DivisionByZeroException"
                    format:@"Cannot divide %ld by zero", (long)numerator];
    }

    return numerator / denominator;
}

+ (NSInteger)safeDivide:(NSInteger)numerator
                    by:(NSInteger)denominator
                 error:(NSError **)error {

    if (denominator == 0) {
        if (error) {
            *error = [NSError errorWithDomain:@"CalculatorError"
                                         code:1001
                                     userInfo:@{
                                         NSLocalizedDescriptionKey: @"Division by zero",
                                         NSLocalizedFailureReasonErrorKey: @"Cannot divide by zero",
                                         @"numerator": @(numerator)
                                     }];
        }
        return 0;
    }

    return numerator / denominator;
}

@end

// MARK: - 4. Nested Exception Handling

@interface NestedExceptionDemo : NSObject

+ (void)innerMethod;
+ (void)middleMethod;
+ (void)outerMethod;

@end

@implementation NestedExceptionDemo

+ (void)innerMethod {
    @throw [NSException exceptionWithName:@"InnerException"
                                   reason:@"Error in inner method"
                                 userInfo:nil];
}

+ (void)middleMethod {
    @try {
        NSLog(@"Middle method: calling inner method");
        [self innerMethod];
    }
    @catch (NSException *exception) {
        NSLog(@"Middle method caught: %@", exception.name);

        // Re-throw with additional context
        @throw [NSException exceptionWithName:@"MiddleException"
                                       reason:[NSString stringWithFormat:@"Wrapped: %@", exception.reason]
                                     userInfo:@{@"originalException": exception.name}];
    }
}

+ (void)outerMethod {
    @try {
        NSLog(@"Outer method: calling middle method");
        [self middleMethod];
    }
    @catch (NSException *exception) {
        NSLog(@"Outer method caught: %@", exception.name);
        NSLog(@"Original exception: %@", exception.userInfo[@"originalException"]);
    }
}

@end

// MARK: - 5. Exception with Resource Management

@interface ResourceManager : NSObject

@property (nonatomic, strong) NSMutableArray *resources;

- (void)allocateResource:(NSString *)resourceName;
- (void)releaseAllResources;
- (void)demonstrateResourceCleanup;

@end

@implementation ResourceManager

- (instancetype)init {
    self = [super init];

    if (self) {
        _resources = [NSMutableArray array];
    }

    return self;
}

- (void)allocateResource:(NSString *)resourceName {
    [self.resources addObject:resourceName];
    NSLog(@"Allocated resource: %@", resourceName);
}

- (void)releaseAllResources {
    for (NSString *resource in self.resources) {
        NSLog(@"Released resource: %@", resource);
    }
    [self.resources removeAllObjects];
}

- (void)demonstrateResourceCleanup {
    @try {
        NSLog(@"--- Resource Management with Exceptions ---");

        [self allocateResource:@"Database Connection"];
        [self allocateResource:@"File Handle"];
        [self allocateResource:@"Network Socket"];

        // Simulate an exception
        if (YES) {
            [NSException raise:@"ResourceException"
                        format:@"Error during resource usage"];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"Exception caught: %@", exception.reason);
    }
    @finally {
        NSLog(@"Cleaning up all resources");
        [self releaseAllResources];
    }

    NSLog(@"\n");
}

@end

// MARK: - Main Demonstration

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"=== macOS Objective-C Exception Handling Examples ===\n");

        // 1. Basic exception handling
        NSLog(@"1. BASIC EXCEPTION HANDLING\n");
        [ExceptionHandler demonstrateBasicException];
        [ExceptionHandler demonstrateMultipleCatchBlocks];
        [ExceptionHandler demonstrateFinallyBlock];

        // 2. Custom exceptions
        NSLog(@"2. CUSTOM EXCEPTIONS\n");
        @try {
            @throw [CustomValidationException exceptionWithReason:@"Cannot be empty"
                                                          field:@"username"
                                                            value:@""];
        }
        @catch (CustomValidationException *e) {
            NSLog(@"Validation Error:");
            NSLog(@"  Field: %@", e.field);
            NSLog(@"  Value: %@", e.value);
            NSLog(@"  Reason: %@", e.reason);
        }

        NSLog(@"\n");

        // 3. Exception in methods
        NSLog(@"3. EXCEPTION IN METHODS\n");

        @try {
            NSInteger result = [SafeCalculator divide:10 by:0 error:nil];
            NSLog(@"Result: %ld", (long)result);
        }
        @catch (NSException *exception) {
            NSLog(@"Caught: %@", exception.reason);
        }

        // Safe version with NSError
        NSError *error = nil;
        NSInteger result = [SafeCalculator safeDivide:10 by:0 error:&error];
        if (error) {
            NSLog(@"Safe method returned error: %@", error.localizedDescription);
            NSLog(@"Result: %ld", (long)result);
        }

        NSLog(@"\n");

        // 4. Nested exception handling
        NSLog(@"4. NESTED EXCEPTION HANDLING\n");
        [NestedExceptionDemo outerMethod];

        NSLog(@"\n");

        // 5. Resource management
        NSLog(@"5. RESOURCE MANAGEMENT\n");
        ResourceManager *manager = [[ResourceManager alloc] init];
        [manager demonstrateResourceCleanup];

        // 6. Exception information
        NSLog(@"6. EXCEPTION INFORMATION\n");
        @try {
            NSDictionary *userInfo = @{
                @"errorCode": @(500),
                @"requestId": @"req-12345",
                @"timestamp": @([NSDate date].timeIntervalSince1970)
            };

            [NSException raise:@"APIException"
                        format:@"API request failed"
                    userInfo:userInfo];
        }
        @catch (NSException *exception) {
            NSLog(@"Exception name: %@", exception.name);
            NSLog(@"Exception reason: %@", exception.reason);
            NSLog(@"User info: %@", exception.userInfo);
            NSLog(@"Call stack symbols:");
            NSArray *callStack = exception.callStackSymbols;
            for (NSString *symbol in callStack) {
                NSLog(@"  %@", symbol);
            }
        }

        NSLog(@"\n=== Exception Handling Examples Completed ===");
    }

    return 0;
}