Exemplos de Operações de Arquivo macOS Objective-C

Exemplos de operações de arquivo macOS Objective-C incluindo leitura/escrita de arquivo de texto, copiar/mover, travessia de diretório e validação de arquivo

💻 Leitura/Escrita de Arquivo de Texto objectivec

🟢 simple ⭐⭐

Ler e escrever arquivos de texto com várias opções de codificação e tratamento de erros

⏱️ 20 min 🏷️ objectivec, macos, file operations, io
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C Text File Read/Write Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Basic Text File Writing

@interface TextFileWriter : NSObject

// Write simple text to file
+ (BOOL)writeText:(NSString *)text toFile:(NSString *)path error:(NSError **)error {
    return [text writeToFile:path
                   atomically:YES
                     encoding:NSUTF8StringEncoding
                        error:error];
}

// Write text with specific encoding
+ (BOOL)writeText:(NSString *)text
           toFile:(NSString *)path
         encoding:(NSStringEncoding)encoding
            error:(NSError **)error {
    BOOL success = [text writeToFile:path
                         atomically:YES
                           encoding:encoding
                              error:error];

    if (success) {
        NSLog(@"Text written with encoding %ld to: %@", (long)encoding, path);
    }

    return success;
}

// Append text to existing file
+ (BOOL)appendText:(NSString *)text toFile:(NSString *)path error:(NSError **)error {
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:path];

    if (fileHandle) {
        // File exists, append to it
        [fileHandle seekToEndOfFile];
        NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding];
        [fileHandle writeData:data];
        [fileHandle closeFile];

        NSLog(@"Text appended to: %@", path);
        return YES;
    } else {
        // File doesn't exist, create new
        return [self writeText:text toFile:path error:error];
    }
}

// Write text with line endings
+ (BOOL)writeLines:(NSArray<NSString *> *)lines
            toFile:(NSString *)path
             error:(NSError **)error {
    NSString *content = [lines componentsJoinedByString:@"\n"];
    BOOL success = [self writeText:content toFile:path error:error];

    if (success) {
        NSLog(@"%lu lines written to: %@", (unsigned long)lines.count, path);
    }

    return success;
}

// Write with NSData (more control)
+ (BOOL)writeData:(NSData *)data toFile:(NSString *)path error:(NSError **)error {
    BOOL success = [[NSFileManager defaultManager] createFileAtPath:path
                                                           contents:data
                                                         attributes:nil];

    if (success) {
        NSLog(@"File created at: %@", path);
    }

    return success;
}

@end

// MARK: - 2. Basic Text File Reading

@interface TextFileReader : NSObject

// Read entire file as string
+ (NSString *)readTextFromFile:(NSString *)path error:(NSError **)error {
    NSString *content = [NSString stringWithContentsOfFile:path
                                                 encoding:NSUTF8StringEncoding
                                                    error:error];

    if (content && !*error) {
        NSLog(@"Read %lu characters from: %@", (unsigned long)content.length, path);
    }

    return content;
}

// Read with encoding detection
+ (NSString *)readTextWithEncodingFromFile:(NSString *)path error:(NSError **)error {
    NSStringEncoding encodings[] = {
        NSUTF8StringEncoding,
        NSUTF16StringEncoding,
        NSASCIIStringEncoding,
        NSISOLatin1StringEncoding
    };

    NSInteger encodingCount = sizeof(encodings) / sizeof(NSStringEncoding);

    for (NSInteger i = 0; i < encodingCount; i++) {
        NSString *content = [NSString stringWithContentsOfFile:path
                                                     encoding:encodings[i]
                                                        error:NULL];
        if (content) {
            NSLog(@"Read file with encoding: %ld", (long)encodings[i]);
            return content;
        }
    }

    if (error) {
        *error = [NSError errorWithDomain:@"ReadError"
                                     code:1
                                 userInfo:@{NSLocalizedDescriptionKey: @"Could not read file with any supported encoding"}];
    }

    return nil;
}

// Read line by line
+ (NSArray<NSString *> *)readLinesFromFile:(NSString *)path error:(NSError **)error {
    NSString *content = [self readTextFromFile:path error:error];
    if (!content) return nil;

    NSArray *lines = [content componentsSeparatedByString:@"\n"];

    NSLog(@"Read %lu lines from: %@", (unsigned long)lines.count, path);

    return lines;
}

// Read with NSFileHandle (for large files)
+ (NSString *)readWithFileHandleFromFile:(NSString *)path error:(NSError **)error {
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];

    if (!fileHandle) {
        if (error) {
            *error = [NSError errorWithDomain:@"ReadError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Failed to open file for reading"}];
        }
        return nil;
    }

    NSData *data = [fileHandle readDataToEndOfFile];
    [fileHandle closeFile];

    NSString *content = [[NSString alloc] initWithData:data
                                              encoding:NSUTF8StringEncoding];

    if (content) {
        NSLog(@"Read %lu bytes using FileHandle", (unsigned long)data.length);
    }

    return content;
}

// Read specific range
+ (NSString *)readRangeFromFile:(NSString *)path
                        start:(NSUInteger)start
                        length:(NSUInteger)length
                         error:(NSError **)error {
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];

    if (!fileHandle) {
        if (error) {
            *error = [NSError errorWithDomain:@"ReadError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Failed to open file"}];
        }
        return nil;
    }

    [fileHandle seekToFileOffset:start];
    NSData *data = [fileHandle readDataOfLength:length];
    [fileHandle closeFile];

    NSString *content = [[NSString alloc] initWithData:data
                                              encoding:NSUTF8StringEncoding];

    if (content) {
        NSLog(@"Read %lu bytes from offset %lu", (unsigned long)length, (unsigned long)start);
    }

    return content;
}

@end

// MARK: - 3. File Info Helper

@interface FileInfoHelper : NSObject

+ (NSDictionary<NSFileAttributeKey, id> *)getAttributesAtPath:(NSString *)path {
    return [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
}

+ (void)printFileInfoAtPath:(NSString *)path {
    NSDictionary *attributes = [self getAttributesAtPath:path];

    if (!attributes) {
        NSLog(@"No file info available for: %@", path);
        return;
    }

    NSLog(@"\n--- File Info: %@ ---", path);

    NSNumber *fileSize = attributes[NSFileSize];
    if (fileSize) {
        NSLog(@"Size: %@ bytes", fileSize);
    }

    NSDate *modifiedDate = attributes[NSFileModificationDate];
    if (modifiedDate) {
        NSLog(@"Modified: %@", modifiedDate);
    }

    NSFileAttributeType fileType = attributes[NSFileType];
    if (fileType) {
        NSLog(@"Type: %@", fileType);
    }
}

@end

// MARK: - 4. Advanced Text Operations

@interface AdvancedTextOperations : NSObject

// Search for text in file
+ (NSArray<NSDictionary *> *)searchInFile:(NSString *)path
                                 forText:(NSString *)searchText
                                    error:(NSError **)error {
    NSArray *lines = [TextFileReader readLinesFromFile:path error:error];
    if (!lines) return nil;

    NSMutableArray *results = [NSMutableArray array];

    [lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL *stop) {
        NSRange range = [line rangeOfString:searchText options:NSCaseInsensitiveSearch];

        if (range.location != NSNotFound) {
            [results addObject:@{
                @"line": line,
                @"lineNumber": @(idx + 1)
            }];
        }
    }];

    return [results copy];
}

// Replace text in file
+ (BOOL)replaceInFile:(NSString *)path
                 text:(NSString *)searchText
             withText:(NSString *)replaceWith
                error:(NSError **)error {
    NSString *content = [TextFileReader readTextFromFile:path error:error];
    if (!content) return NO;

    NSString *modified = [content stringByReplacingOccurrencesOfString:searchText
                                                           withString:replaceWith];

    BOOL success = [TextFileWriter writeText:modified toFile:path error:error];

    if (success) {
        NSLog(@"Replaced occurrences of '%@' in: %@", searchText, path);
    }

    return success;
}

// Count words in file
+ (NSInteger)countWordsInFile:(NSString *)path error:(NSError **)error {
    NSString *content = [TextFileReader readTextFromFile:path error:error];
    if (!content) return 0;

    NSArray *words = [content componentsSeparatedByCharactersInSet:
                      [NSCharacterSet whitespaceAndNewlineCharacterSet]];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"length > 0"];
    NSArray *nonEmptyWords = [words filteredArrayUsingPredicate:predicate];

    return nonEmptyWords.count;
}

// Read and parse CSV
+ (NSArray<NSArray<NSString *> *> *)readCSVFromFile:(NSString *)path error:(NSError **)error {
    NSString *content = [TextFileReader readTextFromFile:path error:error];
    if (!content) return nil;

    NSArray *lines = [content componentsSeparatedByString:@"\n"];

    NSMutableArray *csvData = [NSMutableArray array];

    for (NSString *line in lines) {
        if (line.length > 0) {
            NSArray *fields = [line componentsSeparatedByString:@","];
            [csvData addObject:fields];
        }
    }

    return [csvData copy];
}

// Write JSON to file
+ (BOOL)writeJSON:(id)jsonObject
           toFile:(NSString *)path
             error:(NSError **)error {
    if (![NSJSONSerialization isValidJSONObject:jsonObject]) {
        if (error) {
            *error = [NSError errorWithDomain:@"JSONError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Invalid JSON object"}];
        }
        return NO;
    }

    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonObject
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:error];

    if (!jsonData) return NO;

    NSString *jsonString = [[NSString alloc] initWithData:jsonData
                                                 encoding:NSUTF8StringEncoding];

    BOOL success = [TextFileWriter writeText:jsonString toFile:path error:error];

    if (success) {
        NSLog(@"JSON written to: %@", path);
    }

    return success;
}

// Read JSON from file
+ (id)readJSONFromFile:(NSString *)path error:(NSError **)error {
    NSString *content = [TextFileReader readTextFromFile:path error:error];
    if (!content) return nil;

    NSData *jsonData = [content dataUsingEncoding:NSUTF8StringEncoding];

    id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData
                                                    options:0
                                                      error:error];

    if (jsonObject) {
        NSLog(@"JSON decoded from: %@", path);
    }

    return jsonObject;
}

@end

// MARK: - 5. Safe File Operations

@interface SafeFileOperations : NSObject

typedef NS_ENUM(NSInteger, FileError) {
    FileErrorNotFound = 1,
    FileErrorPermissionDenied = 2,
    FileErrorInvalidEncoding = 3,
    FileErrorWriteFailed = 4
};

// Safe read with comprehensive error handling
+ (NSString *)safeReadFromFile:(NSString *)path error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:path]) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:FileErrorNotFound
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"File not found: %@", path]}];
        }
        return nil;
    }

    if (![fileManager isReadableFileAtPath:path]) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:FileErrorPermissionDenied
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Permission denied: %@", path]}];
        }
        return nil;
    }

    return [TextFileReader readTextFromFile:path error:error];
}

// Safe write with backup
+ (BOOL)safeWrite:(NSString *)text
           toFile:(NSString *)path
      createBackup:(BOOL)createBackup
             error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // Create backup if requested and file exists
    if (createBackup && [fileManager fileExistsAtPath:path]) {
        NSString *backupPath = [path stringByAppendingString:@".backup"];

        if (![fileManager copyItemAtPath:path toPath:backupPath error:error]) {
            return NO;
        }

        NSLog(@"Backup created at: %@", backupPath);
    }

    return [TextFileWriter writeText:text toFile:path error:error];
}

@end

// MARK: - 6. Encoding Handler

@interface EncodingHandler : NSObject

// Detect file encoding
+ (NSStringEncoding)detectEncodingForFile:(NSString *)path {
    NSStringEncoding encodings[] = {
        NSUTF8StringEncoding,
        NSUTF16StringEncoding,
        NSUTF16BigEndianStringEncoding,
        NSUTF32StringEncoding,
        NSASCIIStringEncoding
    };

    NSInteger encodingCount = sizeof(encodings) / sizeof(NSStringEncoding);

    for (NSInteger i = 0; i < encodingCount; i++) {
        NSString *content = [NSString stringWithContentsOfFile:path
                                                     encoding:encodings[i]
                                                        error:NULL];
        if (content) {
            NSLog(@"Detected encoding: %ld", (long)encodings[i]);
            return encodings[i];
        }
    }

    return NSUTF8StringEncoding; // Default
}

// Convert file encoding
+ (BOOL)convertEncodingForFile:(NSString *)path
                   toEncoding:(NSStringEncoding)targetEncoding
                        error:(NSError **)error {
    NSStringEncoding sourceEncoding = [self detectEncodingForFile:path];

    NSString *content = [NSString stringWithContentsOfFile:path
                                                 encoding:sourceEncoding
                                                    error:error];

    if (!content) return NO;

    BOOL success = [content writeToFile:path
                             atomically:YES
                               encoding:targetEncoding
                                  error:error];

    if (success) {
        NSLog(@"Converted encoding from %ld to %ld",
              (long)sourceEncoding, (long)targetEncoding);
    }

    return success;
}

@end

// MARK: - Main Demonstration

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"=== macOS Objective-C Text File Read/Write Examples ===\n");

        NSString *testFile = @"/tmp/test_text_file.txt";
        NSString *testContent = @"Hello, World!\nThis is a test file.\nCreated with Objective-C on macOS.";

        // 1. Basic write
        NSLog(@"--- 1. Basic Write ---");
        NSError *error = nil;
        BOOL success = [TextFileWriter writeText:testContent toFile:testFile error:&error];

        if (!success) {
            NSLog(@"Error writing file: %@", error.localizedDescription);
        }

        // 2. Basic read
        NSLog(@"\n--- 2. Basic Read ---");
        NSString *content = [TextFileReader readTextFromFile:testFile error:&error];

        if (content) {
            NSLog(@"Content:\n%@", content);
        } else {
            NSLog(@"Error reading file: %@", error.localizedDescription);
        }

        // 3. Read lines
        NSLog(@"\n--- 3. Read Lines ---");
        NSArray *lines = [TextFileReader readLinesFromFile:testFile error:&error];

        if (lines) {
            NSLog(@"%lu lines:", (unsigned long)lines.count);
            [lines enumerateObjectsUsingBlock:^(NSString *line, NSUInteger idx, BOOL *stop) {
                NSLog(@"  %lu: %@", (unsigned long)(idx + 1), line);
            }];
        }

        // 4. Append text
        NSLog(@"\n--- 4. Append Text ---");
        success = [TextFileWriter appendText:@"\nAppended line!" toFile:testFile error:&error];

        if (success) {
            NSString *updated = [TextFileReader readTextFromFile:testFile error:&error];
            NSArray *updatedLines = [updated componentsSeparatedByString:@"\n"];
            NSLog(@"Updated content (last line): %@",
                  [updatedLines lastObject]);
        }

        // 5. File info
        NSLog(@"\n--- 5. File Info ---");
        [FileInfoHelper printFileInfoAtPath:testFile];

        // 6. Word count
        NSLog(@"\n--- 6. Word Count ---");
        NSInteger wordCount = [AdvancedTextOperations countWordsInFile:testFile error:&error];
        NSLog(@"Total words: %ld", (long)wordCount);

        // 7. Search in file
        NSLog(@"\n--- 7. Search in File ---");
        NSArray *results = [AdvancedTextOperations searchInFile:testFile
                                                      forText:@"macOS"
                                                         error:&error];
        NSLog(@"Found %lu occurrences of 'macOS':", (unsigned long)results.count);

        for (NSDictionary *result in results) {
            NSLog(@"  Line %@: %@", result[@"lineNumber"], result[@"line"]);
        }

        // 8. JSON operations
        NSLog(@"\n--- 8. JSON Operations ---");
        NSDictionary *user = @{
            @"name": @"John Doe",
            @"email": @"[email protected]",
            @"age": @30
        };

        NSString *jsonFile = @"/tmp/user.json";
        success = [AdvancedTextOperations writeJSON:user toFile:jsonFile error:&error];

        if (success) {
            NSDictionary *decodedUser = [AdvancedTextOperations readJSONFromFile:jsonFile error:&error];
            NSLog(@"Decoded: %@ - %@", decodedUser[@"name"], decodedUser[@"email"]);
        }

        // 9. Safe operations
        NSLog(@"\n--- 9. Safe Operations ---");
        NSString *safeRead = [SafeFileOperations safeReadFromFile:testFile error:&error];

        if (safeRead) {
            NSLog(@"Safe read successful: %@...",
                  [safeRead substringToIndex:MIN(50, safeRead.length)]);
        }

        // 10. Encoding detection
        NSLog(@"\n--- 10. Encoding Detection ---");
        NSStringEncoding encoding = [EncodingHandler detectEncodingForFile:testFile];
        NSLog(@"Detected encoding: %ld", (long)encoding);

        NSLog(@"\n=== All Text File Operations Completed ===");
    }

    return 0;
}

💻 Validação de Arquivo objectivec

🟢 simple ⭐⭐⭐

Verificar existência de arquivo, obter informações de arquivo, validar tipos de arquivo e comparar arquivos

⏱️ 20 min 🏷️ objectivec, macos, file operations, validation
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C File Validation Examples
// Using Foundation framework

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>

// MARK: - 1. Basic File Existence Check

@interface FileChecker : NSObject

// Check if file exists
+ (BOOL)fileExistsAtPath:(NSString *)path {
    return [[NSFileManager defaultManager] fileExistsAtPath:path];
}

// Check if path is a file (not directory)
+ (BOOL)isFileAtPath:(NSString *)path {
    BOOL isDirectory = NO;
    BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory];
    return exists && !isDirectory;
}

// Check if path is a directory
+ (BOOL)isDirectoryAtPath:(NSString *)path {
    BOOL isDirectory = NO;
    BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory];
    return exists && isDirectory;
}

// Check if file is readable
+ (BOOL)isReadableAtPath:(NSString *)path {
    return [[NSFileManager defaultManager] isReadableFileAtPath:path];
}

// Check if file is writable
+ (BOOL)isWritableAtPath:(NSString *)path {
    return [[NSFileManager defaultManager] isWritableFileAtPath:path];
}

// Check if file is deletable
+ (BOOL)isDeletableAtPath:(NSString *)path {
    return [[NSFileManager defaultManager] isDeletableFileAtPath:path];
}

// Comprehensive file check
+ (NSDictionary *)checkFileAtPath:(NSString *)path {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSMutableDictionary *result = [NSMutableDictionary dictionary];

    if (![fileManager fileExistsAtPath:path]) {
        result[@"status"] = @"notFound";
        result[@"isReadable"] = @NO;
        result[@"isWritable"] = @NO;
        result[@"isExecutable"] = @NO;
        return [result copy];
    }

    BOOL isDirectory = NO;
    [fileManager fileExistsAtPath:path isDirectory:&isDirectory];

    result[@"status"] = isDirectory ? @"directory" : @"file";
    result[@"isReadable"] = @([fileManager isReadableFileAtPath:path]);
    result[@"isWritable"] = @([fileManager isWritableFileAtPath:path]);
    result[@"isExecutable"] = @([fileManager isExecutableFileAtPath:path]);

    return [result copy];
}

@end

// MARK: - 2. File Information

@interface FileInfo : NSObject

// Get complete file attributes
+ (NSDictionary<NSFileAttributeKey, id> *)getAttributesAtPath:(NSString *)path {
    return [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
}

// Get file size
+ (unsigned long long)getSizeAtPath:(NSString *)path {
    NSDictionary *attributes = [self getAttributesAtPath:path];
    return [attributes[NSFileSize] unsignedLongLongValue];
}

// Get creation date
+ (NSDate *)getCreationDateAtPath:(NSString *)path {
    NSDictionary *attributes = [self getAttributesAtPath:path];
    return attributes[NSFileCreationDate];
}

// Get modification date
+ (NSDate *)getModificationDateAtPath:(NSString *)path {
    NSDictionary *attributes = [self getAttributesAtPath:path];
    return attributes[NSFileModificationDate];
}

// Get file extension
+ (NSString *)getExtensionAtPath:(NSString *)path {
    return [path pathExtension];
}

// Get file name without extension
+ (NSString *)getNameWithoutExtensionAtPath:(NSString *)path {
    return [path stringByDeletingPathExtension];
}

// Get MIME type
+ (NSString *)getMIMETypeAtPath:(NSString *)path {
    NSString *extension = [self getExtensionAtPath:path];
    return [self MIMETypeForExtension:extension];
}

+ (NSString *)MIMETypeForExtension:(NSString *)extension {
    NSDictionary *mimeTypes = @{
        @"html": @"text/html",
        @"htm": @"text/html",
        @"css": @"text/css",
        @"js": @"application/javascript",
        @"json": @"application/json",
        @"xml": @"application/xml",
        @"txt": @"text/plain",
        @"png": @"image/png",
        @"jpg": @"image/jpeg",
        @"jpeg": @"image/jpeg",
        @"gif": @"image/gif",
        @"pdf": @"application/pdf",
        @"zip": @"application/zip"
    };

    return mimeTypes[extension.lowercaseString] ?: @"application/octet-stream";
}

@end

// MARK: - 3. File Type Validation

@interface FileTypeValidator : NSObject

// Validate by extension
+ (BOOL)isValidExtensionAtPath:(NSString *)path
               validExtensions:(NSArray<NSString *> *)extensions {
    NSString *ext = [[path pathExtension] lowercaseString];
    return [extensions containsObject:ext];
}

// Validate image file
+ (BOOL)isImageFileAtPath:(NSString *)path {
    NSArray *imageExtensions = @[@"jpg", @"jpeg", @"png", @"gif", @"bmp", @"tiff", @"webp", @"ico"];
    return [self isValidExtensionAtPath:path validExtensions:imageExtensions];
}

// Validate text file
+ (BOOL)isTextFileAtPath:(NSString *)path {
    NSArray *textExtensions = @[@"txt", @"md", @"html", @"css", @"js", @"json", @"xml", @"csv"];
    return [self isValidExtensionAtPath:path validExtensions:textExtensions];
}

// Check if file is empty
+ (BOOL)isEmptyFileAtPath:(NSString *)path {
    unsigned long long size = [FileInfo getSizeAtPath:path];
    return size == 0;
}

// Validate file size constraints
+ (BOOL)isSizeValidAtPath:(NSString *)path
                  minSize:(unsigned long long)minSize
                  maxSize:(unsigned long long)maxSize {
    unsigned long long size = [FileInfo getSizeAtPath:path];
    return size >= minSize && size <= maxSize;
}

@end

// MARK: - 4. File Comparison

@interface FileComparator : NSObject

// Compare files by size
+ (NSComparisonResult)compareSizeAtPath:(NSString *)path1
                               andPath:(NSString *)path2 {
    unsigned long long size1 = [FileInfo getSizeAtPath:path1];
    unsigned long long size2 = [FileInfo getSizeAtPath:path2];

    if (size1 < size2) {
        return NSOrderedAscending;
    } else if (size1 > size2) {
        return NSOrderedDescending;
    } else {
        return NSOrderedSame;
    }
}

// Compare files by content
+ (BOOL)compareContentAtPath:(NSString *)path1 andPath:(NSString *)path2 {
    NSData *data1 = [NSData dataWithContentsOfFile:path1];
    NSData *data2 = [NSData dataWithContentsOfFile:path2];

    if (!data1 || !data2) return NO;

    return [data1 isEqualToData:data2];
}

// Compare files by modification date
+ (NSComparisonResult)compareModificationDateAtPath:(NSString *)path1
                                            andPath:(NSString *)path2 {
    NSDate *date1 = [FileInfo getModificationDateAtPath:path1];
    NSDate *date2 = [FileInfo getModificationDateAtPath:path2];

    return [date1 compare:date2];
}

// Check if files are identical
+ (BOOL)areFilesIdenticalAtPath:(NSString *)path1 andPath:(NSString *)path2 {
    NSComparisonResult sizeResult = [self compareSizeAtPath:path1 andPath:path2];

    if (sizeResult != NSOrderedSame) {
        return NO;
    }

    return [self compareContentAtPath:path1 andPath:path2];
}

// Calculate file checksum (SHA256)
+ (NSString *)calculateChecksumAtPath:(NSString *)path {
    NSData *data = [NSData dataWithContentsOfFile:path];

    if (!data) return nil;

    uint8_t digest[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256(data.bytes, (CC_LONG)data.length, digest);

    NSMutableString *hash = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];

    for (NSInteger i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
        [hash appendFormat:@"%02x", digest[i]];
    }

    return [hash copy];
}

// Compare using checksum
+ (BOOL)compareChecksumAtPath:(NSString *)path1 andPath:(NSString *)path2 {
    NSString *hash1 = [self calculateChecksumAtPath:path1];
    NSString *hash2 = [self calculateChecksumAtPath:path2];

    return [hash1 isEqualToString:hash2];
}

@end

// MARK: - 5. Path Validation

@interface PathValidator : NSObject

// Validate path format
+ (BOOL)isValidPath:(NSString *)path {
    return path.length > 0 && [path isAbsolutePath];
}

// Get absolute path from relative
+ (NSString *)getAbsolutePathForPath:(NSString *)path {
    return [[NSFileManager defaultManager] currentDirectoryPath];
}

// Resolve path with tilde
+ (NSString *)resolveTildeInPath:(NSString *)path {
    return [path stringByExpandingTildeInPath];
}

// Validate file name
+ (BOOL)isValidFileName:(NSString *)name {
    NSCharacterSet *invalidChars = [NSCharacterSet characterSetWithCharactersInString:@"\\/:*?\"<>|"];

    return [name rangeOfCharacterFromSet:invalidChars].location == NSNotFound && name.length > 0;
}

// Sanitize file name
+ (NSString *)sanitizeFileName:(NSString *)name {
    NSCharacterSet *invalidChars = [NSCharacterSet characterSetWithCharactersInString:@"\\/:*?\"<>|"];

    return [[name componentsSeparatedByCharactersInSet:invalidChars]
            componentsJoinedByString:@"_"];
}

@end

// MARK: - 6. File Integrity Checker

@interface FileIntegrityChecker : NSObject

// Verify file hasn't been corrupted
+ (BOOL)verifyIntegrityAtPath:(NSString *)path
              againstExpectedHash:(NSString *)expectedHash {
    NSString *actualHash = [FileComparator calculateChecksumAtPath:path];

    if (!actualHash) return NO;

    return [actualHash caseInsensitiveCompare:expectedHash] == NSOrderedSame;
}

// Generate hash file for integrity checking
+ (NSString *)generateHashFileAtPath:(NSString *)path {
    return [FileComparator calculateChecksumAtPath:path];
}

// Quick check (just size)
+ (BOOL)quickCheckAtPath:(NSString *)path expectedSize:(unsigned long long)expectedSize {
    unsigned long long actualSize = [FileInfo getSizeAtPath:path];
    return actualSize == expectedSize;
}

@end

// MARK: - Helper Functions

static NSString *formatBytes(unsigned long long bytes) {
    double kb = bytes / 1024.0;
    double mb = kb / 1024.0;
    double gb = mb / 1024.0;

    if (gb >= 1) {
        return [NSString stringWithFormat:@"%.2f GB", gb];
    } else if (mb >= 1) {
        return [NSString stringWithFormat:@"%.2f MB", mb];
    } else if (kb >= 1) {
        return [NSString stringWithFormat:@"%.2f KB", kb];
    } else {
        return [NSString stringWithFormat:@"%llu B", bytes];
    }
}

static NSString *formatDate(NSDate *date) {
    static NSDateFormatter *formatter = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        formatter = [[NSDateFormatter alloc] init];
        formatter.dateStyle = NSDateFormatterMediumStyle;
        formatter.timeStyle = NSDateFormatterShortStyle;
    });

    return [formatter stringFromDate:date];
}

// MARK: - Main Demonstration

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

        NSString *testFile = @"/tmp/validation_test.txt";
        NSString *testDir = @"/tmp/validation_test_dir";

        // Create test file
        NSFileManager *fileManager = [NSFileManager defaultManager];
        [@"Test content for validation" writeToFile:testFile
                                        atomically:YES
                                          encoding:NSUTF8StringEncoding
                                             error:nil];
        [fileManager createDirectoryAtPath:testDir
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];

        // 1. File existence checks
        NSLog(@"--- 1. File Existence Checks ---");
        NSLog(@"File exists: %@", [FileChecker fileExistsAtPath:testFile] ? @"YES" : @"NO");
        NSLog(@"Is file: %@", [FileChecker isFileAtPath:testFile] ? @"YES" : @"NO");
        NSLog(@"Is directory: %@", [FileChecker isDirectoryAtPath:testDir] ? @"YES" : @"NO");
        NSLog(@"Is readable: %@", [FileChecker isReadableAtPath:testFile] ? @"YES" : @"NO");
        NSLog(@"Is writable: %@", [FileChecker isWritableAtPath:testFile] ? @"YES" : @"NO");

        // 2. Comprehensive file check
        NSLog(@"\n--- 2. Comprehensive File Check ---");
        NSDictionary *status = [FileChecker checkFileAtPath:testFile];
        NSLog(@"Status: %@", status[@"status"]);
        NSLog(@"Readable: %@", status[@"isReadable"]);
        NSLog(@"Writable: %@", status[@"isWritable"]);
        NSLog(@"Executable: %@", status[@"isExecutable"]);

        // 3. File information
        NSLog(@"\n--- 3. File Information ---");
        unsigned long long size = [FileInfo getSizeAtPath:testFile];
        NSLog(@"Size: %@ %@", formatBytes(size), @"bytes");

        NSDate *modDate = [FileInfo getModificationDateAtPath:testFile];
        NSLog(@"Modified: %@", formatDate(modDate));

        NSLog(@"Extension: %@", [FileInfo getExtensionAtPath:testFile]);
        NSLog(@"Name without ext: %@", [FileInfo getNameWithoutExtensionAtPath:testFile]);

        // 4. File type validation
        NSLog(@"\n--- 4. File Type Validation ---");
        NSLog(@"Is text file: %@", [FileTypeValidator isTextFileAtPath:testFile] ? @"YES" : @"NO");
        NSLog(@"Is image file: %@", [FileTypeValidator isImageFileAtPath:testFile] ? @"YES" : @"NO");
        NSLog(@"Is empty: %@", [FileTypeValidator isEmptyFileAtPath:testFile] ? @"YES" : @"NO");
        NSLog(@"Size valid (1-1000 bytes): %@",
              [FileTypeValidator isSizeValidAtPath:testFile minSize:1 maxSize:1000] ? @"YES" : @"NO");

        // 5. File comparison
        NSLog(@"\n--- 5. File Comparison ---");
        NSString *testFile2 = @"/tmp/validation_test2.txt";
        [@"Different content" writeToFile:testFile2
                           atomically:YES
                             encoding:NSUTF8StringEncoding
                                error:nil];

        NSComparisonResult sizeCompare = [FileComparator compareSizeAtPath:testFile andPath:testFile2];
        NSLog(@"Size comparison: %@", sizeCompare == NSOrderedAscending ? @"less than" :
              sizeCompare == NSOrderedDescending ? @"greater than" : @"equal");

        BOOL contentEqual = [FileComparator compareContentAtPath:testFile andPath:testFile2];
        NSLog(@"Content equal: %@", contentEqual ? @"YES" : @"NO");

        // 6. Checksum
        NSLog(@"\n--- 6. Checksum ---");
        NSString *checksum = [FileComparator calculateChecksumAtPath:testFile];
        NSLog(@"SHA256: %@", checksum);

        BOOL valid = [FileIntegrityChecker verifyIntegrityAtPath:testFile
                                        againstExpectedHash:checksum];
        NSLog(@"Integrity check: %@", valid ? @"PASSED" : @"FAILED");

        // 7. Path validation
        NSLog(@"\n--- 7. Path Validation ---");
        NSString *relativePath = @"./test.txt";
        NSLog(@"Is absolute path: %@", [PathValidator isValidPath:testFile] ? @"YES" : @"NO");
        NSLog(@"Valid filename 'test.txt': %@", [PathValidator isValidFileName:@"test.txt"] ? @"YES" : @"NO");
        NSLog(@"Valid filename 'test?.txt': %@", [PathValidator isValidFileName:@"test?.txt"] ? @"YES" : @"NO");
        NSLog(@"Sanitized name: %@", [PathValidator sanitizeFileName:@"test?.txt"]);

        // Cleanup
        [fileManager removeItemAtPath:testFile error:nil];
        [fileManager removeItemAtPath:testFile2 error:nil];
        [fileManager removeItemAtPath:testDir error:nil];
        NSLog(@"\nCleanup completed");

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

    return 0;
}

💻 Copiar/Mover Arquivos objectivec

🟡 intermediate ⭐⭐⭐

Copiar e mover arquivos com opções de sobrescrita, rastreamento de progresso e recuperação de erros

⏱️ 25 min 🏷️ objectivec, macos, file operations
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C File Copy/Move Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Basic File Copy

@interface FileCopier : NSObject

// Simple copy
+ (BOOL)copyFileFrom:(NSString *)source to:(NSString *)destination error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    BOOL success = [fileManager copyItemAtPath:source toPath:destination error:error];

    if (success) {
        NSLog(@"Copied: %@ -> %@", source, destination);
    }

    return success;
}

// Copy with overwrite option
+ (BOOL)copyFileFrom:(NSString *)source
                  to:(NSString *)destination
           overwrite:(BOOL)overwrite
                error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // Check if destination exists
    if ([fileManager fileExistsAtPath:destination]) {
        if (overwrite) {
            // Remove existing file
            [fileManager removeItemAtPath:destination error:nil];
            NSLog(@"Removed existing file: %@", destination);
        } else {
            if (error) {
                *error = [NSError errorWithDomain:@"FileError"
                                             code:1
                                         userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"File already exists: %@", destination]}];
            }
            return NO;
        }
    }

    return [self copyFileFrom:source to:destination error:error];
}

// Copy to directory
+ (BOOL)copyFileFrom:(NSString *)source toDirectory:(NSString *)directory error:(NSError **)error {
    NSString *fileName = [source lastPathComponent];
    NSString *destination = [directory stringByAppendingPathComponent:fileName];

    return [self copyFileFrom:source to:destination error:error];
}

// Copy with NSURL
+ (BOOL)copyFileFromURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // Create destination directory if needed
    NSString *destPath = [destinationURL path];
    NSString *destDir = [destPath stringByDeletingLastPathComponent];

    if (![fileManager fileExistsAtPath:destDir]) {
        [fileManager createDirectoryAtPath:destDir
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];
    }

    BOOL success = [fileManager copyItemAtURL:sourceURL toURL:destinationURL error:error];

    if (success) {
        NSLog(@"Copied: %@ -> %@", [sourceURL path], [destinationURL path]);
    }

    return success;
}

// Copy with progress callback
+ (BOOL)copyFileFrom:(NSString *)source
                  to:(NSString *)destination
             progress:(void (^)(double progress))progressBlock
                error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:source]) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Source file not found: %@", source]}];
        }
        return NO;
    }

    NSDictionary *attributes = [fileManager attributesOfItemAtPath:source error:error];
    if (!attributes) return NO;

    unsigned long long fileSize = [attributes[NSFileSize] unsignedLongLongValue];

    // Read source in chunks
    NSFileHandle *sourceHandle = [NSFileHandle fileHandleForReadingAtPath:source];
    if (!sourceHandle) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Failed to open source file"}];
        }
        return NO;
    }

    // Create destination
    [fileManager createFileAtPath:destination contents:nil attributes:nil];
    NSFileHandle *destHandle = [NSFileHandle fileHandleForWritingAtPath:destination];

    if (!destHandle) {
        [sourceHandle closeFile];
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Failed to create destination file"}];
        }
        return NO;
    }

    const NSUInteger chunkSize = 1024 * 1024; // 1MB chunks
    unsigned long long bytesCopied = 0;

    while (YES) {
        @autoreleasepool {
            NSData *data = [sourceHandle readDataOfLength:chunkSize];

            if (data.length == 0) break;

            [destHandle writeData:data];
            bytesCopied += data.length;

            // Report progress
            if (progressBlock) {
                double percent = (double)bytesCopied / (double)fileSize;
                progressBlock(percent);
            }
        }
    }

    [sourceHandle closeFile];
    [destHandle closeFile];

    NSLog(@"Copy completed: %@", destination);

    return YES;
}

@end

// MARK: - 2. Basic File Move

@interface FileMover : NSObject

// Simple move
+ (BOOL)moveFileFrom:(NSString *)source to:(NSString *)destination error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    BOOL success = [fileManager moveItemAtPath:source toPath:destination error:error];

    if (success) {
        NSLog(@"Moved: %@ -> %@", source, destination);
    }

    return success;
}

// Move with overwrite
+ (BOOL)moveFileFrom:(NSString *)source
                  to:(NSString *)destination
           overwrite:(BOOL)overwrite
                error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if ([fileManager fileExistsAtPath:destination]) {
        if (overwrite) {
            [fileManager removeItemAtPath:destination error:nil];
            NSLog(@"Removed existing file: %@", destination);
        } else {
            if (error) {
                *error = [NSError errorWithDomain:@"FileError"
                                             code:1
                                         userInfo:@{NSLocalizedDescriptionKey: @"File already exists"}];
            }
            return NO;
        }
    }

    return [self moveFileFrom:source to:destination error:error];
}

// Move to directory
+ (BOOL)moveFileFrom:(NSString *)source toDirectory:(NSString *)directory error:(NSError **)error {
    NSString *fileName = [source lastPathComponent];
    NSString *destination = [directory stringByAppendingPathComponent:fileName];

    return [self moveFileFrom:source to:destination error:error];
}

// Safe move with backup
+ (BOOL)moveFileFrom:(NSString *)source
                  to:(NSString *)destination
         withBackup:(BOOL)createBackup
                error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // Create backup if destination exists
    if (createBackup && [fileManager fileExistsAtPath:destination]) {
        NSString *backupPath = [destination stringByAppendingString:@".backup"];

        if (![fileManager moveItemAtPath:destination toPath:backupPath error:error]) {
            return NO;
        }

        NSLog(@"Backup created: %@", backupPath);
    }

    return [self moveFileFrom:source to:destination error:error];
}

// Move with NSURL
+ (BOOL)moveFileFromURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // Create destination directory if needed
    NSString *destPath = [destinationURL path];
    NSString *destDir = [destPath stringByDeletingLastPathComponent];

    if (![fileManager fileExistsAtPath:destDir]) {
        [fileManager createDirectoryAtPath:destDir
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];
    }

    BOOL success = [fileManager moveItemAtURL:sourceURL toURL:destinationURL error:error];

    if (success) {
        NSLog(@"Moved: %@ -> %@", [sourceURL path], [destinationURL path]);
    }

    return success;
}

@end

// MARK: - 3. Batch Operations

@interface BatchFileOperations : NSObject

// Copy multiple files
+ (void)copyFiles:(NSArray<NSString *> *)sources
toDirectory:(NSString *)destinationDirectory
         error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // Ensure destination directory exists
    if (![fileManager fileExistsAtPath:destinationDirectory]) {
        [fileManager createDirectoryAtPath:destinationDirectory
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];
    }

    NSInteger successCount = 0;
    NSInteger failureCount = 0;

    for (NSString *source in sources) {
        NSString *fileName = [source lastPathComponent];
        NSString *destination = [destinationDirectory stringByAppendingPathComponent:fileName];

        NSError *localError = nil;
        if ([FileCopier copyFileFrom:source to:destination overwrite:YES error:&localError]) {
            successCount++;
        } else {
            NSLog(@"Failed to copy %@: %@", source, localError.localizedDescription);
            failureCount++;
        }
    }

    NSLog(@"Batch copy completed: %ld succeeded, %ld failed",
          (long)successCount, (long)failureCount);
}

// Move multiple files
+ (void)moveFiles:(NSArray<NSString *> *)sources
toDirectory:(NSString *)destinationDirectory
         error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if (![fileManager fileExistsAtPath:destinationDirectory]) {
        [fileManager createDirectoryAtPath:destinationDirectory
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];
    }

    NSInteger successCount = 0;
    NSInteger failureCount = 0;

    for (NSString *source in sources) {
        NSString *fileName = [source lastPathComponent];
        NSString *destination = [destinationDirectory stringByAppendingPathComponent:fileName];

        NSError *localError = nil;
        if ([FileMover moveFileFrom:source to:destination overwrite:YES error:&localError]) {
            successCount++;
        } else {
            NSLog(@"Failed to move %@: %@", source, localError.localizedDescription);
            failureCount++;
        }
    }

    NSLog(@"Batch move completed: %ld succeeded, %ld failed",
          (long)successCount, (long)failureCount);
}

// Copy directory recursively
+ (BOOL)copyDirectoryFrom:(NSString *)source to:(NSString *)destination error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    BOOL isDirectory = NO;
    if (![fileManager fileExistsAtPath:source isDirectory:&isDirectory] || !isDirectory) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Not a directory"}];
        }
        return NO;
    }

    BOOL success = [fileManager copyItemAtPath:source toPath:destination error:error];

    if (success) {
        NSLog(@"Directory copied: %@ -> %@", source, destination);
    }

    return success;
}

// Move directory
+ (BOOL)moveDirectoryFrom:(NSString *)source to:(NSString *)destination error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    BOOL isDirectory = NO;
    if (![fileManager fileExistsAtPath:source isDirectory:&isDirectory] || !isDirectory) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Not a directory"}];
        }
        return NO;
    }

    BOOL success = [fileManager moveItemAtPath:source toPath:destination error:error];

    if (success) {
        NSLog(@"Directory moved: %@ -> %@", source, destination);
    }

    return success;
}

@end

// MARK: - 4. Smart File Operations

@interface SmartFileOperations : NSObject

// Copy with duplicate handling
+ (BOOL)smartCopyFrom:(NSString *)source to:(NSString *)destination error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    if ([fileManager fileExistsAtPath:destination]) {
        // Add number suffix
        NSString *baseName = [destination stringByDeletingPathExtension];
        NSString *extension = [destination pathExtension];

        NSInteger counter = 1;
        NSString *newPath = destination;

        while ([fileManager fileExistsAtPath:newPath]) {
            if (extension.length > 0) {
                newPath = [NSString stringWithFormat:@"%@ %ld.%@",
                          baseName, (long)counter, extension];
            } else {
                newPath = [NSString stringWithFormat:@"%@ %ld",
                          baseName, (long)counter];
            }
            counter++;
        }

        NSLog(@"Destination exists, using: %@", newPath);
        return [FileCopier copyFileFrom:source to:newPath error:error];
    }

    return [FileCopier copyFileFrom:source to:destination error:error];
}

// Verified copy
+ (BOOL)verifiedCopyFrom:(NSString *)source to:(NSString *)destination error:(NSError **)error {
    NSDictionary *sourceAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:source error:error];

    if (!sourceAttrs) return NO;

    BOOL success = [FileCopier copyFileFrom:source to:destination overwrite:YES error:error];

    if (!success) return NO;

    // Verify by comparing file sizes
    NSDictionary *destAttrs = [[NSFileManager defaultManager] attributesOfItemAtPath:destination error:error];

    if (!destAttrs) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"Failed to get destination attributes"}];
        }
        return NO;
    }

    unsigned long long sourceSize = [sourceAttrs[NSFileSize] unsignedLongLongValue];
    unsigned long long destSize = [destAttrs[NSFileSize] unsignedLongLongValue];

    if (sourceSize != destSize) {
        if (error) {
            *error = [NSError errorWithDomain:@"FileError"
                                         code:1
                                     userInfo:@{NSLocalizedDescriptionKey: @"File sizes don't match"}];
        }
        return NO;
    }

    NSLog(@"Verified copy: %llu bytes", sourceSize);
    return YES;
}

@end

// MARK: - Helper Functions

static NSString *formatBytes(unsigned long long bytes) {
    double kb = bytes / 1024.0;
    double mb = kb / 1024.0;
    double gb = mb / 1024.0;

    if (gb >= 1) {
        return [NSString stringWithFormat:@"%.2f GB", gb];
    } else if (mb >= 1) {
        return [NSString stringWithFormat:@"%.2f MB", mb];
    } else if (kb >= 1) {
        return [NSString stringWithFormat:@"%.2f KB", kb];
    } else {
        return [NSString stringWithFormat:@"%llu B", bytes];
    }
}

// MARK: - Main Demonstration

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

        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSString *tempDir = @"/tmp/file_operations_test";

        // Create test directory
        [fileManager createDirectoryAtPath:tempDir
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];

        // Create test file
        NSString *sourceFile = @"/tmp/source_test.txt";
        NSString *destFile1 = @"/tmp/dest_test1.txt";
        NSString *destFile2 = @"/tmp/dest_test2.txt";

        NSString *testContent = @"Test content for copy/move";
        [testContent writeToFile:sourceFile
                      atomically:YES
                        encoding:NSUTF8StringEncoding
                           error:nil];

        // 1. Simple copy
        NSLog(@"--- 1. Simple Copy ---");
        NSError *error = nil;
        BOOL success = [FileCopier copyFileFrom:sourceFile to:destFile1 error:&error];

        if (!success) {
            NSLog(@"Error: %@", error.localizedDescription);
        }

        // 2. Copy with progress
        NSLog(@"\n--- 2. Copy with Progress ---");
        success = [FileCopier copyFileFrom:sourceFile
                                          to:destFile2
                                     progress:^(double progress) {
            NSLog(@"Progress: %.0f%%", progress * 100);
        } error:&error];

        if (!success) {
            NSLog(@"Error: %@", error.localizedDescription);
        }

        // 3. Simple move
        NSLog(@"\n--- 3. Simple Move ---");
        NSString *moveSource = @"/tmp/move_source.txt";
        NSString *moveDest = @"/tmp/move_dest.txt";

        [@"File to move" writeToFile:moveSource
                       atomically:YES
                         encoding:NSUTF8StringEncoding
                            error:nil];

        success = [FileMover moveFileFrom:moveSource to:moveDest error:&error];

        if (!success) {
            NSLog(@"Error: %@", error.localizedDescription);
        }

        // 4. Smart copy (handle duplicates)
        NSLog(@"\n--- 4. Smart Copy ---");
        NSString *smartSource = @"/tmp/smart_source.txt";
        NSString *smartDest = @"/tmp/smart_dest.txt";

        [@"Smart copy test" writeToFile:smartSource
                         atomically:YES
                           encoding:NSUTF8StringEncoding
                              error:nil];

        success = [SmartFileOperations smartCopyFrom:smartSource to:smartDest error:&error];
        success = [SmartFileOperations smartCopyFrom:smartSource to:smartDest error:&error];

        // 5. Batch copy
        NSLog(@"\n--- 5. Batch Copy ---");
        NSMutableArray *files = [NSMutableArray array];

        for (int i = 1; i <= 3; i++) {
            NSString *file = [NSString stringWithFormat:@"/tmp/batch_source%d.txt", i];
            [NSString stringWithFormat:@"Batch file %d", i] writeToFile:file
                                                       atomically:YES
                                                         encoding:NSUTF8StringEncoding
                                                            error:nil];
            [files addObject:file];
        }

        [BatchFileOperations copyFiles:files toDirectory:tempDir error:&error];

        // 6. Verified copy
        NSLog(@"\n--- 6. Verified Copy ---");
        NSString *verifySource = @"/tmp/verify_source.txt";
        NSString *verifyDest = @"/tmp/verify_dest.txt";

        [@"Verification test" writeToFile:verifySource
                           atomically:YES
                             encoding:NSUTF8StringEncoding
                                error:nil];

        success = [SmartFileOperations verifiedCopyFrom:verifySource to:verifyDest error:&error];

        if (!success) {
            NSLog(@"Error: %@", error.localizedDescription);
        }

        // Cleanup
        [fileManager removeItemAtPath:tempDir error:nil];
        NSLog(@"\nCleanup completed");

        NSLog(@"\n=== All File Copy/Move Examples Completed ===");
    }

    return 0;
}

💻 Travessia de Diretório objectivec

🟡 intermediate ⭐⭐⭐⭐

Percorrer diretórios recursivamente, listar arquivos com filtros e procurar tipos de arquivo específicos

⏱️ 30 min 🏷️ objectivec, macos, file operations, directory
Prerequisites: Intermediate Objective-C, Foundation framework
// macOS Objective-C Directory Traversal Examples
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Basic Directory Listing

@interface DirectoryLister : NSObject

// List all items in directory
+ (NSArray<NSString *> *)listDirectoryAtPath:(NSString *)path error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *contents = [fileManager contentsOfDirectoryAtPath:path error:error];

    if (contents) {
        NSLog(@"Items in %@:", path);
        for (NSString *item in contents) {
            NSLog(@"  - %@", item);
        }
    }

    return contents;
}

// List with details
+ (NSArray<NSDictionary *> *)listDirectoryWithDetailsAtPath:(NSString *)path error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *contents = [fileManager contentsOfDirectoryAtPath:path error:error];

    if (!contents) return nil;

    NSMutableArray *items = [NSMutableArray array];

    for (NSString *item in contents) {
        NSString *itemPath = [path stringByAppendingPathComponent:item];
        BOOL isDirectory = NO;

        [fileManager fileExistsAtPath:itemPath isDirectory:&isDirectory];

        NSMutableDictionary *fileInfo = [NSMutableDictionary dictionary];
        fileInfo[@"name"] = item;
        fileInfo[@"path"] = itemPath;
        fileInfo[@"isDirectory"] = @(isDirectory);

        NSDictionary *attributes = [fileManager attributesOfItemAtPath:itemPath error:nil];
        fileInfo[@"size"] = attributes[NSFileSize] ?: @(0);
        fileInfo[@"modifiedDate"] = attributes[NSFileModificationDate] ?: [NSDate date];

        [items addObject:fileInfo];
    }

    NSLog(@"Files in %@:", path);
    for (NSDictionary *item in items) {
        NSString *type = [item[@"isDirectory"] boolValue] ? @"DIR " : @"FILE";
        NSLog(@"  [%@] %@ - %@ bytes",
              type, item[@"name"], item[@"size"]);
    }

    return [items copy];
}

// List only files
+ (NSArray<NSString *> *)listFilesAtPath:(NSString *)path error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *contents = [fileManager contentsOfDirectoryAtPath:path error:error];

    if (!contents) return nil;

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *item, NSDictionary *bindings) {
        NSString *itemPath = [path stringByAppendingPathComponent:item];
        BOOL isDirectory = NO;
        [fileManager fileExistsAtPath:itemPath isDirectory:&isDirectory];
        return !isDirectory;
    }];

    return [contents filteredArrayUsingPredicate:predicate];
}

// List only subdirectories
+ (NSArray<NSString *> *)listSubdirectoriesAtPath:(NSString *)path error:(NSError **)error {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *contents = [fileManager contentsOfDirectoryAtPath:path error:error];

    if (!contents) return nil;

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *item, NSDictionary *bindings) {
        NSString *itemPath = [path stringByAppendingPathComponent:item];
        BOOL isDirectory = NO;
        [fileManager fileExistsAtPath:itemPath isDirectory:&isDirectory];
        return isDirectory;
    }];

    return [contents filteredArrayUsingPredicate:predicate];
}

@end

// MARK: - 2. Recursive Directory Traversal

@interface DirectoryTraverser : NSObject

// Recursively find all files
+ (NSArray<NSString *> *)findAllFilesAtPath:(NSString *)path {
    NSMutableArray *files = [NSMutableArray array];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *contents = [fileManager contentsOfDirectoryAtPath:path error:nil];

    if (!contents) {
        return [files copy];
    }

    for (NSString *item in contents) {
        NSString *itemPath = [path stringByAppendingPathComponent:item];
        BOOL isDirectory = NO;

        [fileManager fileExistsAtPath:itemPath isDirectory:&isDirectory];

        if (isDirectory) {
            // Recurse into subdirectory
            [files addObjectsFromArray:[self findAllFilesAtPath:itemPath]];
        } else {
            [files addObject:itemPath];
        }
    }

    return [files copy];
}

// Recursive traversal with callback
+ (void)traverseDirectoryAtPath:(NSString *)path
                       callback:(void (^)(NSString *path, BOOL isDirectory, NSInteger depth))callback {
    [self traverseDirectoryAtPath:path depth:0 callback:callback];
}

+ (void)traverseDirectoryAtPath:(NSString *)path
                         depth:(NSInteger)depth
                      callback:(void (^)(NSString *path, BOOL isDirectory, NSInteger depth))callback {
    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSArray *contents = [fileManager contentsOfDirectoryAtPath:path error:nil];

    if (!contents) return;

    for (NSString *item in contents) {
        NSString *itemPath = [path stringByAppendingPathComponent:item];
        BOOL isDirectory = NO;

        [fileManager fileExistsAtPath:itemPath isDirectory:&isDirectory];

        callback(itemPath, isDirectory, depth);

        if (isDirectory) {
            [self traverseDirectoryAtPath:itemPath
                                     depth:depth + 1
                                  callback:callback];
        }
    }
}

// Get directory tree
+ (DirectoryNode *)getDirectoryTreeAtPath:(NSString *)path maxDepth:(NSInteger)maxDepth {
    DirectoryNode *root = [[DirectoryNode alloc] initWithName:[path lastPathComponent]
                                                             path:path
                                                         isDirectory:YES
                                                            depth:0];

    [self buildTreeForNode:root maxDepth:maxDepth];

    return root;
}

+ (void)buildTreeForNode:(DirectoryNode *)node maxDepth:(NSInteger)maxDepth {
    if (node.depth >= maxDepth) return;

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *contents = [fileManager contentsOfDirectoryAtPath:node.path error:nil];

    if (!contents) return;

    NSArray *sortedContents = [contents sortedArrayUsingSelector:@selector(compare:)];

    for (NSString *item in sortedContents) {
        NSString *itemPath = [node.path stringByAppendingPathComponent:item];
        BOOL isDirectory = NO;

        [fileManager fileExistsAtPath:itemPath isDirectory:&isDirectory];

        DirectoryNode *childNode = [[DirectoryNode alloc] initWithName:item
                                                                    path:itemPath
                                                                isDirectory:isDirectory
                                                                   depth:node.depth + 1];

        [node addChild:childNode];

        if (isDirectory) {
            [self buildTreeForNode:childNode maxDepth:maxDepth];
        }
    }
}

@end

// Directory Node Class
@interface DirectoryNode : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *path;
@property (nonatomic, assign) BOOL isDirectory;
@property (nonatomic, assign) NSInteger depth;
@property (nonatomic, strong) NSMutableArray<DirectoryNode *> *children;

- (instancetype)initWithName:(NSString *)name
                        path:(NSString *)path
                  isDirectory:(BOOL)isDirectory
                       depth:(NSInteger)depth;

- (void)addChild:(DirectoryNode *)child;
- (void)printTree;

@end

@implementation DirectoryNode

- (instancetype)initWithName:(NSString *)name
                        path:(NSString *)path
                  isDirectory:(BOOL)isDirectory
                       depth:(NSInteger)depth {
    self = [super init];

    if (self) {
        _name = name;
        _path = path;
        _isDirectory = isDirectory;
        _depth = depth;
        _children = [NSMutableArray array];
    }

    return self;
}

- (void)addChild:(DirectoryNode *)child {
    [_children addObject:child];
}

- (void)printTree {
    NSString *indent = [@"" stringByPaddingToLength:_depth * 2
                                        withString:@"  "
                                   startingAtIndex:0];

    NSString *prefix = _isDirectory ? @"📁" : @"📄";
    NSLog(@"%@%@ %@", indent, prefix, _name);

    for (DirectoryNode *child in _children) {
        [child printTree];
    }
}

@end

// MARK: - 3. File Filtering

@interface FileFilter : NSObject

// Filter by extension
+ (NSArray<NSString *> *)filterFilesAtPath:(NSString *)path
                                extensions:(NSArray<NSString *> *)extensions {
    NSArray *files = [DirectoryTraverser findAllFilesAtPath:path];

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *filePath, NSDictionary *bindings) {
        NSString *ext = [[filePath pathExtension] lowercaseString];
        return [extensions containsObject:ext];
    }];

    return [files filteredArrayUsingPredicate:predicate];
}

// Filter by name pattern
+ (NSArray<NSString *> *)filterFilesAtPath:(NSString *)path
                              namePattern:(NSString *)pattern {
    NSArray *files = [DirectoryTraverser findAllFilesAtPath:path];

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *filePath, NSDictionary *bindings) {
        NSString *fileName = [filePath lastPathComponent];
        NSRange range = [fileName rangeOfString:pattern
                                         options:NSRegularExpressionSearch
                                           range:NSMakeRange(0, fileName.length)];
        return range.location != NSNotFound;
    }];

    return [files filteredArrayUsingPredicate:predicate];
}

// Filter by size
+ (NSArray<NSString *> *)filterFilesAtPath:(NSString *)path
                                  minSize:(unsigned long long)minSize
                                  maxSize:(unsigned long long)maxSize {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *files = [DirectoryTraverser findAllFilesAtPath:path];

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *filePath, NSDictionary *bindings) {
        NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
        if (!attributes) return NO;

        unsigned long long size = [attributes[NSFileSize] unsignedLongLongValue];
        return size >= minSize && size <= maxSize;
    }];

    return [files filteredArrayUsingPredicate:predicate];
}

// Find duplicates by size
+ (NSArray<NSArray<NSString *> *> *)findPotentialDuplicatesAtPath:(NSString *)path {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *files = [DirectoryTraverser findAllFilesAtPath:path];

    NSMutableDictionary *sizeGroups = [NSMutableDictionary dictionary];

    for (NSString *filePath in files) {
        NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
        if (!attributes) continue;

        unsigned long long size = [attributes[NSFileSize] unsignedLongLongValue];
        NSString *key = [NSString stringWithFormat:@"%llu", size];

        if (!sizeGroups[key]) {
            sizeGroups[key] = [NSMutableArray array];
        }

        [sizeGroups[key] addObject:filePath];
    }

    // Filter groups with more than one file
    NSMutableArray *duplicates = [NSMutableArray array];

    for (NSMutableArray *group in [sizeGroups allValues]) {
        if (group.count > 1) {
            [duplicates addObject:[group copy]];
        }
    }

    return [duplicates copy];
}

@end

// MARK: - 4. Directory Statistics

@interface DirectoryStatistics : NSObject

+ (NSDictionary *)calculateStatisticsForPath:(NSString *)path {
    NSMutableDictionary *stats = [NSMutableDictionary dictionary];

    stats[@"totalFiles"] = @0;
    stats[@"totalDirectories"] = @0;
    stats[@"totalSize"] = @0;
    stats[@"extensionCounts"] = [NSMutableDictionary dictionary];

    [DirectoryTraverser traverseDirectoryAtPath:path
                                       callback:^(NSString *filePath, BOOL isDirectory, NSInteger depth) {
        if (isDirectory) {
            NSInteger count = [stats[@"totalDirectories"] integerValue];
            stats[@"totalDirectories"] = @(count + 1);
        } else {
            NSInteger count = [stats[@"totalFiles"] integerValue];
            stats[@"totalFiles"] = @(count + 1);

            NSFileManager *fileManager = [NSFileManager defaultManager];
            NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];

            if (attributes) {
                unsigned long long size = [attributes[NSFileSize] unsignedLongLongValue];

                unsigned long long currentSize = [stats[@"totalSize"] unsignedLongLongValue];
                stats[@"totalSize"] = @(currentSize + size);

                // Count extensions
                NSString *ext = [[filePath pathExtension] lowercaseString];
                NSString *extKey = ext.length > 0 ? ext : @"(no extension)";

                NSMutableDictionary *extCounts = stats[@"extensionCounts"];
                NSInteger extCount = [extCounts[extKey] integerValue];
                extCounts[extKey] = @(extCount + 1);
            }
        }
    }];

    return [stats copy];
}

+ (void)printStatistics:(NSDictionary *)stats {
    NSLog(@"\n=== Directory Statistics ===");

    NSInteger totalFiles = [stats[@"totalFiles"] integerValue];
    NSInteger totalDirectories = [stats[@"totalDirectories"] integerValue];
    unsigned long long totalSize = [stats[@"totalSize"] unsignedLongLongValue];

    NSLog(@"Total files: %ld", (long)totalFiles);
    NSLog(@"Total directories: %ld", (long)totalDirectories);
    NSLog(@"Total size: %@", formatBytes(totalSize));

    NSLog(@"\nExtension counts:");
    NSDictionary *extCounts = stats[@"extensionCounts"];

    NSArray *sortedKeys = [extCounts keysSortedByValueUsingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) {
        return [obj2 compare:obj1];
    }];

    for (NSString *ext in sortedKeys) {
        NSInteger count = [extCounts[ext] integerValue];
        NSLog(@"  .%@: %ld", ext, (long)count);
    }
}

static NSString *formatBytes(unsigned long long bytes) {
    double kb = bytes / 1024.0;
    double mb = kb / 1024.0;
    double gb = mb / 1024.0;

    if (gb >= 1) {
        return [NSString stringWithFormat:@"%.2f GB", gb];
    } else if (mb >= 1) {
        return [NSString stringWithFormat:@"%.2f MB", mb];
    } else if (kb >= 1) {
        return [NSString stringWithFormat:@"%.2f KB", kb];
    } else {
        return [NSString stringWithFormat:@"%llu B", bytes];
    }
}

@end

// MARK: - 5. File Searcher

@interface FileSearcher : NSObject

// Search files by name
+ (NSArray<NSString *> *)searchFilesAtPath:(NSString *)path forName:(NSString *)name {
    NSArray *files = [DirectoryTraverser findAllFilesAtPath:path];

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *filePath, NSDictionary *bindings) {
        NSString *fileName = [filePath lastPathComponent];
        NSRange range = [fileName rangeOfString:name
                                         options:NSCaseInsensitiveSearch
                                           range:NSMakeRange(0, fileName.length)];
        return range.location != NSNotFound;
    }];

    return [files filteredArrayUsingPredicate:predicate];
}

// Search files containing text
+ (NSArray<NSString *> *)searchFilesAtPath:(NSString *)path forText:(NSString *)searchText {
    NSArray *files = [DirectoryTraverser findAllFilesAtPath:path];
    NSMutableArray *matches = [NSMutableArray array];

    for (NSString *filePath in files) {
        NSString *content = [NSString stringWithContentsOfFile:filePath
                                                     encoding:NSUTF8StringEncoding
                                                        error:nil];

        if (content) {
            NSRange range = [content rangeOfString:searchText
                                         options:NSCaseInsensitiveSearch];
            if (range.location != NSNotFound) {
                [matches addObject:filePath];
            }
        }
    }

    return [matches copy];
}

// Find recently modified files
+ (NSArray<NSString *> *)findRecentlyModifiedFilesAtPath:(NSString *)path
                                              within:(NSTimeInterval)seconds {
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *files = [DirectoryTraverser findAllFilesAtPath:path];
    NSDate *cutoffDate = [NSDate dateWithTimeIntervalSinceNow:-seconds];

    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *filePath, NSDictionary *bindings) {
        NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil];
        if (!attributes) return NO;

        NSDate *modifiedDate = attributes[NSFileModificationDate];
        return [modifiedDate compare:cutoffDate] == NSOrderedDescending;
    }];

    return [files filteredArrayUsingPredicate:predicate];
}

@end

// MARK: - Main Demonstration

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

        NSString *testDir = @"/tmp/directory_traversal_test";
        NSFileManager *fileManager = [NSFileManager defaultManager];

        // Create test directory structure
        [fileManager createDirectoryAtPath:testDir
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];

        // Create test files
        [@"" writeToFile:[testDir stringByAppendingPathComponent:@"file1.txt"]
            atomically:YES
              encoding:NSUTF8StringEncoding
                 error:nil];

        [@"" writeToFile:[testDir stringByAppendingPathComponent:@"file2.md"]
            atomically:YES
              encoding:NSUTF8StringEncoding
                 error:nil];

        // Create subdirectory
        NSString *subDir = [testDir stringByAppendingPathComponent:@"subdir"];
        [fileManager createDirectoryAtPath:subDir
               withIntermediateDirectories:YES
                                attributes:nil
                                     error:nil];

        [@"" writeToFile:[subDir stringByAppendingPathComponent:@"subfile1.txt"]
            atomically:YES
              encoding:NSUTF8StringEncoding
                 error:nil];

        // 1. List directory
        NSLog(@"--- 1. List Directory ---");
        NSError *error = nil;
        NSArray *items = [DirectoryLister listDirectoryAtPath:testDir error:&error];

        // 2. List with details
        NSLog(@"\n--- 2. List with Details ---");
        NSArray *detailedItems = [DirectoryLister listDirectoryWithDetailsAtPath:testDir error:&error];

        // 3. Recursive traversal
        NSLog(@"\n--- 3. Recursive Traversal ---");
        NSArray *allFiles = [DirectoryTraverser findAllFilesAtPath:testDir];

        NSLog(@"All files:");
        for (NSString *file in allFiles) {
            NSLog(@"  - %@", file);
        }

        // 4. Directory tree
        NSLog(@"\n--- 4. Directory Tree ---");
        DirectoryNode *tree = [DirectoryTraverser getDirectoryTreeAtPath:testDir maxDepth:2];
        [tree printTree];

        // 5. Filter by extension
        NSLog(@"\n--- 5. Filter by Extension ---");
        NSArray *txtFiles = [FileFilter filterFilesAtPath:testDir
                                              extensions:@[@"txt"]];

        NSLog(@"Text files:");
        for (NSString *file in txtFiles) {
            NSLog(@"  - %@", file);
        }

        // 6. Directory statistics
        NSLog(@"\n--- 6. Directory Statistics ---");
        NSDictionary *stats = [DirectoryStatistics calculateStatisticsForPath:testDir];
        [DirectoryStatistics printStatistics:stats];

        // 7. Search by name
        NSLog(@"\n--- 7. Search by Name ---");
        NSArray *searchResults = [FileSearcher searchFilesAtPath:testDir forName:@"file"];

        NSLog(@"Files containing 'file':");
        for (NSString *result in searchResults) {
            NSLog(@"  - %@", result);
        }

        // Cleanup
        [fileManager removeItemAtPath:testDir error:nil];
        NSLog(@"\nCleanup completed");

        NSLog(@"\n=== All Directory Traversal Examples Completed ===");
    }

    return 0;
}