PubSub+ Messaging API For C  7.31.0.7
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ex/ios/examples/AppReachabilityExample.m
/*
* Copyright 2014-2024 Solace Corporation. All rights reserved.
*/
#import "AppReachabilityExample.h"
@implementation AppReachabilityExample
@synthesize session_p;
@synthesize reachability;
@synthesize currentNetworkStatus;
@synthesize currentConnectionRequired;
@synthesize receivedReconnectedEvent;
@synthesize isThreadBlocked;
- (id)initWithExampleInterface:(ExampleInterface *)exampleInterface {
self = [super initWithExampleInterface:exampleInterface];
// Set example name and description
self.name = @"AppReachability";
self.description =
@"Demonstrates how to respond to changes in network state on iOS";
// Setup example parameters
[self.parameters addParameter:PARAMETER_NUM_MESSAGES];
[self.parameters addParameter:PARAMETER_DESTINATION_TOPIC];
[self.parameters parameterWithId:PARAMETER_NUM_MESSAGES].value = @"100";
// Setup Reachability API to receive events
self.reachability = [Reachability reachabilityForInternetConnection];
[self.reachability startNotifier];
self.currentNetworkStatus = [self.reachability currentReachabilityStatus];
self.currentConnectionRequired = [self.reachability connectionRequired];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:NULL];
self.receivedReconnectedEvent = NO;
return self;
}
- (void)eventCallbackWithSession:(solClient_opaqueSession_pt)opaqueSession_p
eventInfo:
userData:(void *)user_p {
// Stop the thread block by changing a state variable when the session
// comes back up
if (self.isThreadBlocked) {
eventInfo_p->sessionEvent ==
receivedReconnectedEvent = YES;
}
}
}
- (void)run {
[super run];
// Session variables
session_p = NULL;
// Context variables
// Message variables
solClient_opaqueMsg_pt msg_p = NULL;
int msgsSent = 0;
// Initialize the API; this must be done prior to first usage
[self handleErrorWithReturnCode:rc
errorString:"solClient_initialize()"];
goto notInitialized;
}
// Set up logging level and log example and API information
[self setLoggingLevel];
"AppReachabilityExample.m (Copyright 2014-2024 Solace Corporation. All rights reserved.)\n");
[self logCCSMPVersion];
// Create a context, and specify that the context thread be created
// automatically instead of having the application create its own
// context thread.
solClient_log(SOLCLIENT_LOG_INFO, "Creating solClient context");
&contextFuncInfo, sizeof(contextFuncInfo))) != SOLCLIENT_OK) {
[self handleErrorWithReturnCode:rc
errorString:"solClient_context_create()"];
goto cleanup;
}
solClient_log(SOLCLIENT_LOG_INFO, "Creating solClient sessions.");
if ((rc = [self createAndConnectSessionWithContext:context_p
session:&session_p
messageCallback:messageReceiveCallback
eventCallback:eventCallback]) !=
[self handleErrorWithReturnCode:
rc errorString:"createAndConnectSessionWithContext:session:"
"messageCallback:eventCallback:userData:"];
goto cleanup;
}
// Ignore initial connection event
receivedReconnectedEvent = NO;
[[self.parameters parameterWithId:PARAMETER_DESTINATION_TOPIC]
.value cStringUsingEncoding:NSASCIIStringEncoding])) !=
// If the last error is connection loss related, block the thread until
// connection is restored (as detected through Reachability)
if ([self isLastErrorConnectionLoss]) {
[self blockThreadDuringConnectionLoss];
// If cancellation is requested, branch to appropriate label;
// otherwise, repeat the while loop to ensure the last command was
// executed
if (self.requestCancel) {
goto sessionConnected;
}
} else {
[self
handleErrorWithReturnCode:rc
errorString:"solClient_session_topicSubscribe()"];
goto sessionConnected;
}
}
solClient_log(SOLCLIENT_LOG_INFO, "Publishing messages.\n");
for (msgsSent = 0;
msgsSent < [self.parameters parameterWithId:PARAMETER_NUM_MESSAGES]
.value.integerValue &&
!self.requestCancel;
++msgsSent) {
// Allocate memory for the message that is to be sent
while ((rc = solClient_msg_alloc(&msg_p)) != SOLCLIENT_OK) {
[self handleErrorWithReturnCode:rc
errorString:"solClient_msg_alloc()"];
goto sessionConnected;
}
// Set the message delivery mode
[self handleErrorWithReturnCode:rc
errorString:"solClient_msg_setDeliveryMode()"];
goto freeMessage;
}
// Set the destination
destination.dest =
[[self.parameters parameterWithId:PARAMETER_DESTINATION_TOPIC].value
cStringUsingEncoding:NSASCIIStringEncoding];
msg_p, &destination, sizeof(destination))) != SOLCLIENT_OK) {
[self handleErrorWithReturnCode:rc
errorString:"solClient_msg_setDestination()"];
goto freeMessage;
}
// Add some content to the message
msg_p, COMMON_ATTACHMENT_TEXT,
(solClient_uint32_t)strlen(COMMON_ATTACHMENT_TEXT))) !=
[self handleErrorWithReturnCode:
rc errorString:"solClient_msg_setBinaryAttachment()"];
goto freeMessage;
}
// Send the message
if ((rc = solClient_session_sendMsg(session_p, msg_p)) !=
// If the last error is connection loss related, block the thread
// until connection is restored (as detected through Reachability)
if ([self isLastErrorConnectionLoss]) {
[self blockThreadDuringConnectionLoss];
// If cancellation is requested, branch to appropriate label;
// otherwise, repeat the while loop to ensure the last command
// was executed
if (self.requestCancel) {
goto freeMessage;
}
} else {
[self handleErrorWithReturnCode:rc
errorString:"solClient_session_sendMsg()"];
goto freeMessage;
}
}
freeMessage:
if ((rc = solClient_msg_free(&msg_p)) != SOLCLIENT_OK) {
[self handleErrorWithReturnCode:rc
errorString:"solClient_msg_free()"];
goto sessionConnected;
}
// Wait one second between sending messages. This provides time for
// the final message to be received.
[NSThread sleepForTimeInterval:1];
}
// Do not unsubscribe if session cancelled
if (self.requestCancel) {
goto sessionConnected;
}
[[self.parameters parameterWithId:PARAMETER_DESTINATION_TOPIC]
.value cStringUsingEncoding:NSASCIIStringEncoding])) !=
// If the last error is connection loss related, block the thread until
// connection is restored (as detected through Reachability)
if ([self isLastErrorConnectionLoss]) {
[self blockThreadDuringConnectionLoss];
// If cancellation is requested, branch to appropriate label;
// otherwise, repeat the while loop to ensure the last command was
// executed
if (self.requestCancel) {
goto sessionConnected;
}
} else {
[self
handleErrorWithReturnCode:rc
errorString:"solClient_session_topicSubscribe()"];
goto sessionConnected;
}
}
sessionConnected:
// Disconnect the session
if ((rc = solClient_session_disconnect(session_p)) != SOLCLIENT_OK) {
[self handleErrorWithReturnCode:rc
errorString:"solClient_session_disconnect()"];
}
cleanup:
// Cleanup solclient
if ((rc = solClient_cleanup()) != SOLCLIENT_OK) {
[self handleErrorWithReturnCode:rc errorString:"solClient_cleanup()"];
}
notInitialized:
[self cleanup];
}
- (BOOL)isLastErrorConnectionLoss {
// Check subcode of last API error to determine reason for disconnection
return YES;
} else {
return NO;
}
}
- (void)blockThreadDuringConnectionLoss {
// Block thread while network is unavailable
"[THREAD BLOCK] Thread blocked while waiting for Reachability "
"reconnection event or cancel request.\n");
self.isThreadBlocked = true;
while (!receivedReconnectedEvent && !self.requestCancel) {
[NSThread sleepForTimeInterval:1];
}
self.isThreadBlocked = false;
// When thread is no longer blocked, reconnect the session (except if the
// cancel button was pressed)
if (self.requestCancel) {
"[THREAD BLOCK] Received cancel request.\n");
} else {
"[THREAD BLOCK] Reachability reconnection event received.\n");
receivedReconnectedEvent = NO;
}
}
- (void)reachabilityChanged:(NSNotification *)note {
Reachability *newReachability = [note object];
NetworkStatus newNetworkStatus =
[newReachability currentReachabilityStatus];
BOOL newConnectionRequired = [newReachability connectionRequired];
// Ignore network changes when the example is not running
if (self.running && self.exampleInterface.runningExample == self) {
if (currentNetworkStatus == NotReachable || currentConnectionRequired) {
"[REACHABILITY] Old network status: Not reachable (or "
"connection required)\n");
// If moving from no connection to WiFi or WWAN, reconnect, since
// session was manually disconnected upon connection loss
if (!newConnectionRequired) {
if (newNetworkStatus == ReachableViaWiFi) {
"[REACHABILITY] New network status: Reachable via "
"Wi-Fi\n");
} else if (newNetworkStatus == ReachableViaWWAN) {
solClient_log(SOLCLIENT_LOG_NOTICE, "[REACHABILITY] New "
"network status: "
"Reachable via WWAN\n");
}
if ((rc = solClient_session_connect(session_p)) !=
[self handleErrorWithReturnCode:
rc errorString:"solClient_session_connect()"];
"[REACHABILITY] Could not reconnect. Cancelling...\n");
[self cancel];
} else {
"[REACHABILITY] Reconnection successful.\n");
}
} else {
"[REACHABILITY] New network status: Not reachable (or "
"connection required)\n");
}
} else if (currentNetworkStatus == ReachableViaWiFi) {
"[REACHABILITY] Old network status: Reachable via Wi-Fi\n");
// If moving from WiFi to no connection, disconnect to prevent
// repeated
// reconnection attempts
if (newNetworkStatus == NotReachable || newConnectionRequired) {
"[REACHABILITY] New network status: Not reachable (or "
"connection required)\n");
if ((rc = solClient_session_disconnect(session_p)) !=
"[REACHABILITY] Could not disconnect.\n");
[self handleErrorWithReturnCode:
rc errorString:"solClient_session_disconnect()"];
} else {
"[REACHABILITY] Disconnection successful.\n");
}
// If moving from WiFi to WWAN, do nothing; the API takes care
// of
// this transition
} else if (newNetworkStatus == ReachableViaWWAN) {
"[REACHABILITY] New network status: Reachable via WWAN\n");
}
} else if (currentNetworkStatus == ReachableViaWWAN) {
"[REACHABILITY] Old network status: Reachable via WWAN\n");
// If moving from WWAN to no connection, disconnect to prevent
// repeated
// reconnection events
if (newNetworkStatus == NotReachable || newConnectionRequired) {
"[REACHABILITY] New network status: Not reachable (or "
"connection required)\n");
if ((rc = solClient_session_disconnect(session_p)) !=
"[REACHABILITY] Could not disconnect.\n");
[self handleErrorWithReturnCode:
rc errorString:"solClient_session_disconnect()"];
} else {
"[REACHABILITY] Disconnection successful.\n");
}
// If moving from WWAN to WiFi, disconnect and reconnect to
// force
// a network interface change
} else if (newNetworkStatus == ReachableViaWiFi) {
"[REACHABILITY] New network status: Reachable via Wi-Fi\n");
if ((rc = solClient_session_disconnect(session_p)) !=
"[REACHABILITY] Could not disconnect.\n");
[self handleErrorWithReturnCode:
rc errorString:"solClient_session_disconnect()"];
} else {
"[REACHABILITY] Disconnection successful.\n");
}
if ((rc = solClient_session_connect(session_p)) !=
[self handleErrorWithReturnCode:
rc errorString:"solClient_session_connect()"];
"[REACHABILITY] Could not reconnect. Cancelling...\n");
[self cancel];
} else {
"[REACHABILITY] Reconnection successful.\n");
}
}
}
}
currentNetworkStatus = newNetworkStatus;
currentConnectionRequired = newConnectionRequired;
}
@end