Ejemplos de Redes macOS Objective-C

Ejemplos de redes macOS Objective-C incluyendo URLSession, solicitudes HTTP, WebSocket y accesibilidad de red

💻 Conceptos Básicos de URLSession objectivec

🟢 simple ⭐⭐⭐

Realizar solicitudes HTTP usando URLSession para descargas de datos, llamadas REST API y cargas de archivos

⏱️ 25 min 🏷️ objectivec, macos, networking, urlsession
Prerequisites: Objective-C basics, Foundation framework
// macOS Objective-C URLSession Basics
// Using Foundation framework

#import <Foundation/Foundation.h>

// MARK: - 1. Simple Data Task

@interface DataTaskDemo : NSObject

+ (void)fetchDataWithURL:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    NSURLSession *session = [NSURLSession sharedSession];

    NSURLSessionDataTask *task = [session dataTaskWithURL:url
                                        completionHandler:^(NSData *data,
                                                            NSURLResponse *response,
                                                            NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error.localizedDescription);
            return;
        }

        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"Status code: %ld", (long)httpResponse.statusCode);

        if (data) {
            NSString *responseString = [[NSString alloc] initWithData:data
                                                            encoding:NSUTF8StringEncoding];
            NSLog(@"Response: %@", responseString);
        }
    }];

    [task resume];
}

+ (void)synchronousFetchWithURL:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    config.timeoutIntervalForRequest = 30.0;

    NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
    NSURLSessionDataTask *task = [session dataTaskWithURL:url];

    [task resume];

    // Wait for completion (blocking)
    while (task.state == NSURLSessionTaskStateRunning) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
}

@end

// MARK: - 2. HTTP POST with JSON

@interface JSONPostDemo : NSObject

+ (void)postJSON:(NSDictionary *)jsonDict
          toURL:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";

    // Set headers
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];

    // Convert JSON to data
    NSError *jsonError = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict
                                                       options:0
                                                         error:&jsonError];

    if (jsonError) {
        NSLog(@"JSON error: %@", jsonError.localizedDescription);
        return;
    }

    request.HTTPBody = jsonData;

    NSURLSession *session = [NSURLSession sharedSession];

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data,
                                                                NSURLResponse *response,
                                                                NSError *error) {
        if (error) {
            NSLog(@"Error: %@", error.localizedDescription);
            return;
        }

        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"Status code: %ld", (long)httpResponse.statusCode);

        if (data) {
            NSError *parseError = nil;
            id jsonResponse = [NSJSONSerialization JSONObjectWithData:data
                                                            options:0
                                                              error:&parseError];

            if (!parseError) {
                NSLog(@"JSON response: %@", jsonResponse);
            }
        }
    }];

    [task resume];
}

@end

// MARK: - 3. Download Task

@interface DownloadDemo : NSObject

+ (void)downloadFileFromURL:(NSString *)urlString
                toPath:(NSString *)destinationPath {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    NSURLSession *session = [NSURLSession sharedSession];

    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url
                                              completionHandler:^(NSURL *location,
                                                                  NSURLResponse *response,
                                                                  NSError *error) {
        if (error) {
            NSLog(@"Download error: %@", error.localizedDescription);
            return;
        }

        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"Downloaded status: %ld", (long)httpResponse.statusCode);

        NSFileManager *fileManager = [NSFileManager defaultManager];

        // Remove existing file
        if ([fileManager fileExistsAtPath:destinationPath]) {
            [fileManager removeItemAtPath:destinationPath error:nil];
        }

        // Move file to destination
        NSError *moveError = nil;
        BOOL success = [fileManager moveItemAtPath:location.path
                                            toPath:destinationPath
                                             error:&moveError];

        if (success) {
            NSLog(@"File saved to: %@", destinationPath);
        } else {
            NSLog(@"Move error: %@", moveError.localizedDescription);
        }
    }];

    [task resume];
}

+ (void)downloadWithProgress:(NSString *)urlString
                  toPath:(NSString *)destinationPath {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:config
                                                          delegate:nil
                                                     delegateQueue:nil];

    // Create download task with progress
    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];

    __block NSURLSessionDownloadTask *weakTask = task;

    [task resume];

    // Monitor progress (simplified)
    NSLog(@"Download started: %@", urlString);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (weakTask.state == NSURLSessionTaskStateRunning) {
            [NSThread sleepForTimeInterval:0.5];
            NSLog(@"Downloading...");
        }
        NSLog(@"Download completed");
    });
}

@end

// MARK: - 4. Upload Task

@interface UploadDemo : NSObject

+ (void)uploadFile:(NSString *)filePath
              toURL:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    // Read file data
    NSError *readError = nil;
    NSData *fileData = [NSData dataWithContentsOfFile:filePath
                                              options:0
                                                error:&readError];

    if (readError) {
        NSLog(@"File read error: %@", readError.localizedDescription);
        return;
    }

    // Create request
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";

    NSString *fileName = [filePath lastPathComponent];
    NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];

    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];

    // Create body
    NSMutableData *body = [NSMutableData data];

    // Add file data
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", fileName] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:fileData];
    [body appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];

    request.HTTPBody = body;

    NSURLSession *session = [NSURLSession sharedSession];

    NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request
                                                       fromData:body
                                              completionHandler:^(NSData *data,
                                                                  NSURLResponse *response,
                                                                  NSError *error) {
        if (error) {
            NSLog(@"Upload error: %@", error.localizedDescription);
            return;
        }

        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"Upload status: %ld", (long)httpResponse.statusCode);

        if (data) {
            NSString *responseString = [[NSString alloc] initWithData:data
                                                            encoding:NSUTF8StringEncoding];
            NSLog(@"Response: %@", responseString);
        }
    }];

    [task resume];
}

@end

// MARK: - 5. Custom Delegate

@interface SessionDelegate : NSObject <NSURLSessionDataDelegate>
@property (nonatomic, strong) NSMutableData *receivedData;
@property (nonatomic, strong) NSURLResponse *urlResponse;

@end

@implementation SessionDelegate

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

    if (self) {
        _receivedData = [NSMutableData data];
    }

    return self;
}

- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    self.urlResponse = response;

    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    NSLog(@"Received response: status %ld", (long)httpResponse.statusCode);

    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data {
    [self.receivedData appendData:data];
    NSLog(@"Received %lu bytes", (unsigned long)data.length);
}

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
    if (error) {
        NSLog(@"Error: %@", error.localizedDescription);
    } else {
        NSLog(@"Task completed. Total bytes: %lu", (unsigned long)self.receivedData.length);

        NSString *responseString = [[NSString alloc] initWithData:self.receivedData
                                                          encoding:NSUTF8StringEncoding];
        NSLog(@"Response: %@", responseString);
    }
}

@end

@interface DelegateDemo : NSObject

+ (void)fetchWithDelegate:(NSString *)urlString {
    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    SessionDelegate *delegate = [[SessionDelegate alloc] init];

    NSURLSession *session = [NSURLSession sessionWithConfiguration:config
                                                          delegate:delegate
                                                     delegateQueue:nil];

    NSURLSessionDataTask *task = [session dataTaskWithURL:url];

    [task resume];

    // Wait for completion
    while (task.state == NSURLSessionTaskStateRunning) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                 beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
}

@end

// MARK: - 6. Background Session

@interface BackgroundSessionDemo : NSObject

+ (NSURLSession *)createBackgroundSession {
    NSString *identifier = @"com.example.background.session";

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration
                                         backgroundSessionConfigurationWithIdentifier:identifier];

    config.discretionary = NO;
    config.sessionSendsLaunchEvents = YES;

    return [NSURLSession sessionWithConfiguration:config];
}

+ (void)startBackgroundDownload:(NSString *)urlString {
    NSURLSession *session = [self createBackgroundSession];

    NSURL *url = [NSURL URLWithString:urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", urlString);
        return;
    }

    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:url];

    [task resume];
    NSLog(@"Background download started");
}

@end

// MARK: - Main Demonstration

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

        // 1. Simple data task
        NSLog(@"--- 1. Simple Data Task ---");
        [DataTaskDemo fetchDataWithURL:@"https://httpbin.org/get"];

        [NSThread sleepForTimeInterval:2.0];

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

        [JSONPostDemo postJSON:jsonDict toURL:@"https://httpbin.org/post"];

        [NSThread sleepForTimeInterval:2.0];

        // 3. Download task
        NSLog(@"\n--- 3. Download Task ---");
        NSString *downloadPath = @"/tmp/downloaded_file.json";

        [DownloadDemo downloadFileFromURL:@"https://httpbin.org/json"
                                   toPath:downloadPath];

        [NSThread sleepForTimeInterval:3.0];

        // 4. Upload task
        NSLog(@"\n--- 4. Upload Task ---");
        NSString *testFile = @"/tmp/test_upload.txt";

        [@"Test upload content" writeToFile:testFile
                                atomically:YES
                                  encoding:NSUTF8StringEncoding
                                     error:nil];

        [UploadDemo uploadFile:testFile toURL:@"https://httpbin.org/post"];

        [NSThread sleepForTimeInterval:2.0];

        // 5. Custom delegate
        NSLog(@"\n--- 5. Custom Delegate ---");
        [DelegateDemo fetchWithDelegate:@"https://httpbin.org/get"];

        // 6. Background session
        NSLog(@"\n--- 6. Background Session ---");
        [BackgroundSessionDemo startBackgroundDownload:@"https://httpbin.org/json"];

        [NSThread sleepForTimeInterval:2.0];

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

    return 0;
}

💻 Comunicación WebSocket objectivec

🟡 intermediate ⭐⭐⭐⭐

Implementar comunicación bidireccional en tiempo real usando WebSocket

⏱️ 30 min 🏷️ objectivec, macos, networking, websocket
Prerequisites: Intermediate Objective-C, Network programming
// macOS Objective-C WebSocket Examples
// Using Foundation and Network framework

#import <Foundation/Foundation.h>

// MARK: - 1. Basic WebSocket Client

@interface WebSocketClient : NSObject <NSStreamDelegate>
@property (nonatomic, strong) NSInputStream *inputStream;
@property (nonatomic, strong) NSOutputStream *outputStream;
@property (nonatomic, strong) NSString *urlString;
@property (nonatomic, assign) BOOL connected;

- (instancetype)initWithURL:(NSString *)urlString;
- (void)connect;
- (void)disconnect;
- (void)sendMessage:(NSString *)message;

@end

@implementation WebSocketClient

- (instancetype)initWithURL:(NSString *)urlString {
    self = [super init];

    if (self) {
        _urlString = urlString;
        _connected = NO;
    }

    return self;
}

- (void)connect {
    NSURL *url = [NSURL URLWithString:self.urlString];

    if (!url) {
        NSLog(@"Invalid URL: %@", self.urlString);
        return;
    }

    NSString *host = url.host;
    NSNumber *port = url.port;

    if (!port) {
        NSString *scheme = url.scheme;
        if ([scheme isEqualToString:@"wss"]) {
            port = @443;
        } else {
            port = @80;
        }
    }

    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    CFStreamCreatePairWithSocketToHost(NULL,
                                        (__bridge CFStringRef)host,
                                        [port unsignedShortValue],
                                        &readStream,
                                        &writeStream);

    if (!readStream || !writeStream) {
        NSLog(@"Failed to create streams");
        return;
    }

    self.inputStream = (__bridge_transfer NSInputStream *)readStream;
    self.outputStream = (__bridge_transfer NSOutputStream *)writeStream;

    self.inputStream.delegate = self;
    self.outputStream.delegate = self;

    [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                                 forMode:NSDefaultRunLoopMode];

    [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                                  forMode:NSDefaultRunLoopMode];

    [self.inputStream open];
    [self.outputStream open];

    NSLog(@"Connecting to %@", self.urlString);
}

- (void)disconnect {
    [self.inputStream close];
    [self.outputStream close];

    [self.inputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
                                forMode:NSDefaultRunLoopMode];

    [self.outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
                                 forMode:NSDefaultRunLoopMode];

    self.inputStream = nil;
    self.outputStream = nil;
    self.connected = NO;

    NSLog(@"Disconnected");
}

- (void)sendMessage:(NSString *)message {
    if (!self.connected || !self.outputStream) {
        NSLog(@"Not connected");
        return;
    }

    NSString *frame = [NSString stringWithFormat:@"\x00%@\xff", message];
    NSData *data = [frame dataUsingEncoding:NSUTF8StringEncoding];

    NSInteger written = [self.outputStream write:data.bytes maxLength:data.length];

    if (written < 0) {
        NSLog(@"Send error");
    } else {
        NSLog(@"Sent: %@", message);
    }
}

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
    switch (eventCode) {
        case NSStreamEventOpenCompleted: {
            if (stream == self.inputStream) {
                self.connected = YES;

                // Send WebSocket handshake
                NSString *handshake = [NSString stringWithFormat:
                    @"GET %@ HTTP/1.1\r\n"
                    @"Host: %@\r\n"
                    @"Upgrade: websocket\r\n"
                    @"Connection: Upgrade\r\n"
                    @"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
                    @"Sec-WebSocket-Version: 13\r\n"
                    @"\r\n",
                    [[NSURL URLWithString:self.urlString] path],
                    [NSURL URLWithString:self.urlString].host
                ];

                NSData *handshakeData = [handshake dataUsingEncoding:NSUTF8StringEncoding];
                [self.outputStream write:handshakeData.bytes maxLength:handshakeData.length];
            }
            break;
        }

        case NSStreamEventHasBytesAvailable: {
            if (stream == self.inputStream) {
                uint8_t buffer[1024];
                NSInteger bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)];

                if (bytesRead > 0) {
                    NSData *data = [NSData dataWithBytes:buffer length:bytesRead];

                    // Parse WebSocket frame (simplified)
                    if (buffer[0] == 0x00) {
                        NSString *message = [[NSString alloc] initWithBytes:buffer + 1
                                                                      length:bytesRead - 2
                                                                    encoding:NSUTF8StringEncoding];
                        NSLog(@"Received: %@", message);
                    } else {
                        NSString *hex = [data description];
                        NSLog(@"Received data: %@", hex);
                    }
                }
            }
            break;
        }

        case NSStreamEventErrorOccurred: {
            NSLog(@"Stream error: %@", stream.streamError);
            [self disconnect];
            break;
        }

        case NSStreamEventEndEncountered: {
            NSLog(@"Stream ended");
            [self disconnect];
            break;
        }

        default:
            break;
    }
}

@end

// MARK: - 2. WebSocket Chat Client

@interface ChatClient : WebSocketClient
@property (nonatomic, strong) NSString *username;
@property (nonatomic, strong) NSMutableArray *messages;

- (instancetype)initWithURL:(NSString *)urlString username:(NSString *)username;
- (void)joinChat;
- (void)sendChatMessage:(NSString *)message;
- (void)printMessages;

@end

@implementation ChatClient

- (instancetype)initWithURL:(NSString *)urlString username:(NSString *)username {
    self = [super initWithURL:urlString];

    if (self) {
        _username = username;
        _messages = [NSMutableArray array];
    }

    return self;
}

- (void)joinChat {
    [self connect];

    // Send join message
    NSString *joinMsg = [NSString stringWithFormat:@"/join %@", self.username];
    [self sendMessage:joinMsg];
}

- (void)sendChatMessage:(NSString *)message {
    NSString *chatMsg = [NSString stringWithFormat:@"/msg %@", message];
    [self sendMessage:chatMsg];

    [self.messages addObject:[NSString stringWithFormat:@"%@: %@", self.username, message]];
}

- (void)printMessages {
    NSLog(@"\n=== Chat Messages ===");
    for (NSString *msg in self.messages) {
        NSLog(@"%@", msg);
    }
    NSLog(@"==================\n");
}

@end

// MARK: - 3. Reconnection Handler

@interface ResilientWebSocketClient : WebSocketClient
@property (nonatomic, assign) NSInteger maxRetries;
@property (nonatomic, assign) NSInteger currentRetry;
@property (nonatomic, assign) NSTimeInterval retryDelay;

- (void)autoConnectWithRetries:(NSInteger)maxRetries delay:(NSTimeInterval)delay;

@end

@implementation ResilientWebSocketClient

- (void)autoConnectWithRetries:(NSInteger)maxRetries delay:(NSTimeInterval)delay {
    self.maxRetries = maxRetries;
    self.retryDelay = delay;
    self.currentRetry = 0;

    [self attemptConnect];
}

- (void)attemptConnect {
    [self connect];

    // Check connection after delay
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.retryDelay * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{
        if (!self.connected && self.currentRetry < self.maxRetries) {
            self.currentRetry++;
            NSLog(@"Connection failed. Retrying (%ld/%ld)...",
                  (long)self.currentRetry, (long)self.maxRetries);

            [self attemptConnect];
        } else if (!self.connected) {
            NSLog(@"Max retries reached. Connection failed.");
        }
    });
}

@end

// MARK: - 4. Heartbeat Protocol

@interface HeartbeatWebSocketClient : WebSocketClient
@property (nonatomic, strong) NSTimer *heartbeatTimer;
@property (nonatomic, assign) NSTimeInterval heartbeatInterval;

- (void)startHeartbeat:(NSTimeInterval)interval;
- (void)stopHeartbeat;

@end

@implementation HeartbeatWebSocketClient

- (void)startHeartbeat:(NSTimeInterval)interval {
    self.heartbeatInterval = interval;

    self.heartbeatTimer = [NSTimer scheduledTimerWithTimeInterval:interval
                                                          repeats:YES
                                                            block:^(NSTimer *timer) {
        [self sendMessage:@"/ping"];
        NSLog(@"Sent heartbeat");
    }];

    NSLog(@"Heartbeat started (%.0f seconds)", interval);
}

- (void)stopHeartbeat {
    [self.heartbeatTimer invalidate];
    self.heartbeatTimer = nil;

    NSLog(@"Heartbeat stopped");
}

- (void)disconnect {
    [self stopHeartbeat];
    [super disconnect];
}

@end

// MARK: - 5. Message Queue

@interface MessageQueueWebSocketClient : WebSocketClient
@property (nonatomic, strong) NSMutableArray *messageQueue;
@property (nonatomic, assign) BOOL shouldQueueMessages;

- (instancetype)initWithURL:(NSString *)urlString;

@end

@implementation MessageQueueWebSocketClient

- (instancetype)initWithURL:(NSString *)urlString {
    self = [super initWithURL:urlString];

    if (self) {
        _messageQueue = [NSMutableArray array];
        _shouldQueueMessages = YES;
    }

    return self;
}

- (void)sendMessage:(NSString *)message {
    if (self.connected) {
        [super sendMessage:message];
    } else if (self.shouldQueueMessages) {
        [self.messageQueue addObject:message];
        NSLog(@"Queued message: %@ (queue size: %lu)",
              message, (unsigned long)self.messageQueue.count);
    }
}

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
    [super stream:stream handleEvent:eventCode];

    if (eventCode == NSStreamEventOpenCompleted && stream == self.inputStream) {
        // Send queued messages
        if (self.messageQueue.count > 0) {
            NSLog(@"Sending %lu queued messages", (unsigned long)self.messageQueue.count);

            for (NSString *queuedMessage in [self.messageQueue copy]) {
                [super sendMessage:queuedMessage];
                [self.messageQueue removeObject:queuedMessage];
            }
        }
    }
}

@end

// MARK: - Main Demonstration

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

        // 1. Basic WebSocket client
        NSLog(@"--- 1. Basic WebSocket Client ---");

        WebSocketClient *client = [[WebSocketClient alloc]
                                    initWithURL:@"ws://echo.websocket.org"];

        [client connect];

        [NSThread sleepForTimeInterval:1.0];

        [client sendMessage:@"Hello, WebSocket!"];
        [client sendMessage:@"How are you?"];

        [NSThread sleepForTimeInterval:2.0];

        [client disconnect];

        // 2. Chat client
        NSLog(@"\n--- 2. Chat Client ---");

        ChatClient *chatClient = [[ChatClient alloc]
                                   initWithURL:@"ws://echo.websocket.org"
                                   username:@"Alice"];

        [chatClient joinChat];

        [NSThread sleepForTimeInterval:1.0];

        [chatClient sendChatMessage:@"Hi everyone!"];
        [chatClient sendChatMessage:@"How's it going?"];

        [NSThread sleepForTimeInterval:2.0];

        [chatClient printMessages];
        [chatClient disconnect];

        // 3. Resilient client with retries
        NSLog(@"\n--- 3. Resilient Client ---");

        ResilientWebSocketClient *resilientClient = [[ResilientWebSocketClient alloc]
                                                      initWithURL:@"ws://invalid.websocket.org"];

        [resilientClient autoConnectWithRetries:3 delay:1.0];

        [NSThread sleepForTimeInterval:5.0);

        // 4. Heartbeat
        NSLog(@"\n--- 4. Heartbeat Protocol ---");

        HeartbeatWebSocketClient *heartbeatClient = [[HeartbeatWebSocketClient alloc]
                                                      initWithURL:@"ws://echo.websocket.org"];

        [heartbeatClient connect];

        [NSThread sleepForTimeInterval:1.0];

        [heartbeatClient startHeartbeat:2.0];

        [NSThread sleepForTimeInterval:8.0);

        [heartbeatClient stopHeartbeat];
        [heartbeatClient disconnect];

        // 5. Message queue
        NSLog(@"\n--- 5. Message Queue ---");

        MessageQueueWebSocketClient *queueClient = [[MessageQueueWebSocketClient alloc]
                                                    initWithURL:@"ws://echo.websocket.org"];

        // Send messages before connected
        [queueClient sendMessage:@"Queued 1"];
        [queueClient sendMessage:@"Queued 2"];
        [queueClient sendMessage:@"Queued 3"];

        [queueClient connect];

        [NSThread sleepForTimeInterval:2.0);

        [queueClient sendMessage:@"After connect"];

        [NSThread sleepForTimeInterval:2.0];

        [queueClient disconnect];

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

    return 0;
}

💻 Accesibilidad de Red objectivec

🟡 intermediate ⭐⭐⭐

Monitorear el estado de conectividad de red y detectar cambios de red

⏱️ 25 min 🏷️ objectivec, macos, networking, reachability
Prerequisites: Intermediate Objective-C, SystemConfiguration framework
// macOS Objective-C Network Reachability Examples
// Using SystemConfiguration framework

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>

// MARK: - 1. Basic Reachability Check

@interface ReachabilityChecker : NSObject

+ (BOOL)isNetworkAvailable {
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));

    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;

    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&zeroAddress);

    if (!reachability) {
        return NO;
    }

    SCNetworkReachabilityFlags flags;
    BOOL success = SCNetworkReachabilityGetFlags(reachability, &flags);

    CFRelease(reachability);

    if (!success) {
        return NO;
    }

    BOOL isReachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
    BOOL needsConnection = (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0;

    return isReachable && !needsConnection;
}

+ (NSString *)getConnectionType {
    struct sockaddr_in zeroAddress;
    bzero(&zeroAddress, sizeof(zeroAddress));

    zeroAddress.sin_len = sizeof(zeroAddress);
    zeroAddress.sin_family = AF_INET;

    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (struct sockaddr *)&zeroAddress);

    if (!reachability) {
        return @"Unknown";
    }

    SCNetworkReachabilityFlags flags;
    BOOL success = SCNetworkReachabilityGetFlags(reachability, &flags);

    CFRelease(reachability);

    if (!success) {
        return @"Unknown";
    }

    if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
        return @"No Connection";
    }

    if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) {
        return @"Cellular";
    }

    return @"WiFi";
}

@end

// MARK: - 2. Reachability Monitor

@interface ReachabilityMonitor : NSObject

@property (nonatomic, assign) SCNetworkReachabilityRef reachability;
@property (nonatomic, strong) NSTimer *pollingTimer;
@property (nonatomic, copy) void (^onChange)(BOOL isReachable, NSString *connectionType);

- (instancetype)initWithCallback:(void (^)(BOOL, NSString *))callback;
- (void)startMonitoring;
- (void)stopMonitoring;

@end

@implementation ReachabilityMonitor

- (instancetype)initWithCallback:(void (^)(BOOL, NSString *))callback {
    self = [super init];

    if (self) {
        _onChange = callback;

        struct sockaddr_in zeroAddress;
        bzero(&zeroAddress, sizeof(zeroAddress));

        zeroAddress.sin_len = sizeof(zeroAddress);
        zeroAddress.sin_family = AF_INET;

        _reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault,
                                                               (struct sockaddr *)&zeroAddress);
    }

    return self;
}

- (void)startMonitoring {
    if (!_reachability) {
        NSLog(@"Reachability not initialized");
        return;
    }

    // Start polling timer
    self.pollingTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
                                                        repeats:YES
                                                          block:^(NSTimer *timer) {
        BOOL isReachable = [ReachabilityChecker isNetworkAvailable];
        NSString *connectionType = [ReachabilityChecker getConnectionType];

        if (self.onChange) {
            self.onChange(isReachable, connectionType);
        }
    }];

    NSLog(@"Started monitoring network reachability");
}

- (void)stopMonitoring {
    [self.pollingTimer invalidate];
    self.pollingTimer = nil;

    NSLog(@"Stopped monitoring network reachability");
}

- (void)dealloc {
    [self stopMonitoring];

    if (_reachability) {
        CFRelease(_reachability);
    }
}

@end

// MARK: - 3. Host Reachability

@interface HostReachabilityChecker : NSObject

+ (BOOL)isHostReachable:(NSString *)hostName;
+ (BOOL)isHostReachableSync:(NSString *)hostName;

@end

@implementation HostReachabilityChecker

+ (BOOL)isHostReachable:(NSString *)hostName {
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault,
                                                                                [hostName UTF8String]);

    if (!reachability) {
        return NO;
    }

    SCNetworkReachabilityFlags flags;
    BOOL success = SCNetworkReachabilityGetFlags(reachability, &flags);

    CFRelease(reachability);

    if (!success) {
        return NO;
    }

    BOOL isReachable = (flags & kSCNetworkReachabilityFlagsReachable) != 0;
    BOOL needsConnection = (flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0;

    return isReachable && !needsConnection;
}

+ (BOOL)isHostReachableSync:(NSString *)hostName {
    // Try to actually connect to the host
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    CFStreamCreatePairWithSocketToHost(NULL,
                                        (__bridge CFStringRef)hostName,
                                        80,
                                        &readStream,
                                        &writeStream);

    if (!readStream || !writeStream) {
        return NO;
    }

    NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
    NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;

    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                          forMode:NSDefaultRunLoopMode];

    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                           forMode:NSDefaultRunLoopMode];

    [inputStream open];
    [outputStream open];

    // Wait for connection
    NSDate *timeout = [NSDate dateWithTimeIntervalSinceNow:3.0];

    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:timeout];

    BOOL reachable = [outputStream streamStatus] == NSStreamStatusOpen;

    [inputStream close];
    [outputStream close];

    return reachable;
}

@end

// MARK: - 4. Connection Quality

@interface ConnectionQuality : NSObject

+ (NSString *)assessConnectionQuality {
    // Simple quality assessment based on connection type
    NSString *connectionType = [ReachabilityChecker getConnectionType];

    if ([connectionType isEqualToString:@"No Connection"]) {
        return @"None";
    } else if ([connectionType isEqualToString:@"WiFi"]) {
        return @"Good";
    } else if ([connectionType isEqualToString:@"Cellular"]) {
        return @"Moderate";
    }

    return @"Unknown";
}

+ (void)runSpeedTest:(void (^)(double speedMbps))completion {
    // Simulated speed test
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSURL *url = [NSURL URLWithString:@"https://httpbin.org/bytes/1048576"];

        NSData *data = [NSData dataWithContentsOfURL:url];

        double speedMbps = 0;

        if (data) {
            // Assume it took 2 seconds for 1 MB
            double mb = data.length / (1024.0 * 1024.0);
            speedMbps = mb / 2.0;
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(speedMbps);
            }
        });
    });
}

@end

// MARK: - 5. Network State Manager

@interface NetworkStateManager : NSObject

@property (nonatomic, assign) BOOL isReachable;
@property (nonatomic, strong) NSString *connectionType;
@property (nonatomic, strong) NSMutableArray<void (^)(BOOL, NSString *)> *observers;

+ (instancetype)sharedManager;
- (void)addObserver:(void (^)(BOOL, NSString *))observer;
- (void)removeObserver:(void (^)(BOOL, NSString *))observer;
- (void)startMonitoring;
- (void)stopMonitoring;

@end

@implementation NetworkStateManager {
    ReachabilityMonitor *_monitor;
}

+ (instancetype)sharedManager {
    static NetworkStateManager *sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[NetworkStateManager alloc] init];
    });

    return sharedInstance;
}

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

    if (self) {
        _isReachable = [ReachabilityChecker isNetworkAvailable];
        _connectionType = [ReachabilityChecker getConnectionType];
        _observers = [NSMutableArray array];

        __weak typeof(self) weakSelf = self;

        _monitor = [[ReachabilityMonitor alloc] initWithCallback:^(BOOL isReachable, NSString *connectionType) {
            __strong typeof(weakSelf) strongSelf = weakSelf;

            if (strongSelf.isReachable != isReachable ||
                ![strongSelf.connectionType isEqualToString:connectionType]) {

                strongSelf.isReachable = isReachable;
                strongSelf.connectionType = connectionType;

                NSLog(@"Network changed: %@ - %@", isReachable ? @"Connected" : @"Disconnected", connectionType);

                [strongSelf notifyObservers];
            }
        }];
    }

    return self;
}

- (void)addObserver:(void (^)(BOOL, NSString *))observer {
    [self.observers addObject:observer];

    // Immediately call with current state
    observer(self.isReachable, self.connectionType);
}

- (void)removeObserver:(void (^)(BOOL, NSString *))observer {
    [self.observers removeObject:observer];
}

- (void)notifyObservers {
    for (void (^observer)(BOOL, NSString *) in self.observers) {
        observer(self.isReachable, self.connectionType);
    }
}

- (void)startMonitoring {
    [_monitor startMonitoring];
}

- (void)stopMonitoring {
    [_monitor stopMonitoring];
}

@end

// MARK: - Main Demonstration

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

        // 1. Basic reachability check
        NSLog(@"--- 1. Basic Reachability Check ---");

        BOOL isAvailable = [ReachabilityChecker isNetworkAvailable];
        NSString *connType = [ReachabilityChecker getConnectionType];

        NSLog(@"Network Available: %@", isAvailable ? @"YES" : @"NO");
        NSLog(@"Connection Type: %@", connType);

        // 2. Host reachability
        NSLog(@"\n--- 2. Host Reachability ---");

        NSArray *hosts = @[@"apple.com", @"google.com", @"github.com", @"localhost"];

        for (NSString *host in hosts) {
            BOOL reachable = [HostReachabilityChecker isHostReachable:host];
            NSLog(@"%@: %@", host, reachable ? @"✓ Reachable" : @"✗ Unreachable");
        }

        // 3. Connection quality
        NSLog(@"\n--- 3. Connection Quality ---");

        NSString *quality = [ConnectionQuality assessConnectionQuality];
        NSLog(@"Connection Quality: %@", quality);

        [ConnectionQuality runSpeedTest:^(double speedMbps) {
            NSLog(@"Speed: %.2f Mbps", speedMbps);
        }];

        [NSThread sleepForTimeInterval:5.0];

        // 4. Reachability monitor
        NSLog(@"\n--- 4. Reachability Monitor ---");

        ReachabilityMonitor *monitor = [[ReachabilityMonitor alloc]
                                       initWithCallback:^(BOOL isReachable, NSString *connectionType) {
            NSLog(@"Status: %@ - %@", isReachable ? @"Connected" : @"Disconnected", connectionType);
        }];

        [monitor startMonitoring];

        NSLog(@"Monitoring for 15 seconds...");
        [NSThread sleepForTimeInterval:15.0];

        [monitor stopMonitoring];

        // 5. Network state manager
        NSLog(@"\n--- 5. Network State Manager ---");

        NetworkStateManager *manager = [NetworkStateManager sharedManager];

        [manager addObserver:^(BOOL isReachable, NSString *connectionType) {
            NSLog(@"Observer 1: %@ - %@", isReachable ? @"Connected" : @"Disconnected", connectionType);
        }];

        [manager addObserver:^(BOOL isReachable, NSString *connectionType) {
            NSLog(@"Observer 2: Quality = %@", [ConnectionQuality assessConnectionQuality]);
        }];

        [manager startMonitoring];

        NSLog(@"State manager monitoring for 10 seconds...");
        [NSThread sleepForTimeInterval:10.0];

        [manager stopMonitoring];

        NSLog(@"\n=== Network Reachability Examples Completed ===");
    }

    return 0;
}