Skip to Content

       Below steps provide you a clear sequence for ios Application  registration process with SUP 2.1.3 server .Basically we need 2 classes to control the same

1) CallbackHandler Class

2) SUPConnection class

Background : Assuming you  have a single sign on enabled in your landscape. For more info on SSO – SUP ,please find the following thread

/community/developer-center/mobility-platform/blog/2012/08/10/how-to-sso-between-sup-and-sap-in-a-cua-environment

We connect through Mobile –> Relay server  –> SUP –> SAP

Step 1 : Create a callback handler class to handle event listeners for your application.This is pretty straight forward and you can find it in SUP101 sample application itself.you can customize callback handlers as you need

#import "SUPCallbackHandler.h"
@implementation SUPCallbackHandler
+ (SUPCallbackHandler*)getInstance
{
    return [[[SUPCallbackHandler alloc] init] autorelease];
}
- (void)sendNotification:(NSNotification *)notification
{
    [[NSNotificationCenter defaultCenter] postNotification:notification];
}
- (void)postNotification:(NSString *)notification withObject:(id)obj;
{
    // All callback notifications other than onSubscribe: will happen on a thread other than the main UI thread. So, if you
    // want to update the UI in response to a callback you need to post the notification from the main thread.
    NSNotification *n = [NSNotification notificationWithName:notification object:obj];
     [n retain];
    [self performSelectorOnMainThread:@selector(sendNotification:) withObject:n waitUntilDone:NO];
}
//*****************************************Methods specific to messaging-based synchronization***********************************
- (void)onConnectionStatusChange:(SUPDeviceConnectionStatus)connStatus :(SUPDeviceConnectionType)connType :(int32_t)errorCode :(NSString *)errorString
{
    NSString *notification = nil;
    switch(connStatus)
    {
        case CONNECTED_NUM:
            notification = ON_CONNECT_SUCCESS;
            break;
        case DISCONNECTED_NUM:
            notification = ON_CONNECT_FAILURE;
            break;
        default:
            // Ignore all other status changes for this example.
            break;
    }
    if (notification != nil) [self postNotification:notification withObject:nil];
    MyLog(@"=================================================");
    MyLog(@"onConnectionStatusChange: status = %d, code = %d, message = %@",connStatus,errorCode,errorString);
    MyLog(@"=================================================");
}
- (void)onApplicationSettingsChanged:(SUPStringList*)names
{
    MyLog(@"================================================");
    MyLog(@"onApplicationSettingsChanged: names = %@",[names toString]);
    MyLog(@"================================================");
}
- (void)onConnectionStatusChanged:(SUPConnectionStatusType)connectionStatus :(int32_t)errorCode :(NSString*)errorMessage
{
    MyLog(@"=================================================");
    MyLog(@"onConnectionStatusChanged: status = %d, code = %d, message = %@",connectionStatus,errorCode,errorMessage);
    MyLog(@"=================================================");
    NSString *notification = nil;
    switch(connectionStatus)
    {
        case SUPConnectionStatus_CONNECTED:
            notification = ON_CONNECT_SUCCESS;
            break;
        case SUPConnectionStatus_DISCONNECTED:
            notification = ON_CONNECT_FAILURE;
            break;
        default:
            // Ignore all other status changes for this example.
            break;
    }
    if (notification != nil) [self postNotification:notification withObject:nil];
}
- (void)onHttpCommunicationError :(int32_t)errorCode :(NSString*)errorMessage :(SUPStringProperties*)responseHeaders
{
    MyLog(@"=================================================");
    MyLog(@"onHttpCommunicationError: errorCode = %i",errorCode);
    MyLog(@"=================================================");
}
- (void)onRegistrationStatusChanged:(SUPRegistrationStatusType)registrationStatus :(SUPInt)errorCode :(SUPNullableString)errorMessage
{
    MyLog(@"=================================================");
    MyLog(@"onRegistrationStatusChanged: status = %d, code = %d, message = %@",registrationStatus,errorCode,errorMessage);
    MyLog(@"=================================================");
   }
- (void)onDeviceConditionChanged :(SUPDeviceConditionType)condition
{
    MyLog(@"=================================================");
    MyLog(@"onDeviceConditionChanged: condition = %d",condition);
    MyLog(@"=================================================");
}
//*********************************SUPApplicationCallback*******************************************************
- (void)onReplaySuccess:(id)theObject
{
    MBOLogInfo(@"================================================");
    MBOLogInfo(@"Replay Successful");
    MBOLogInfo(@"=================================================");
    [self postNotification:ON_REPLAY_SUCCESS withObject:theObject];
}
- (void)onReplayFailure:(id)theObject
{
    MBOLogInfo(@"================================================");
    MBOLogInfo(@"Replay Failure");
    MBOLogInfo(@"=================================================");
    [self postNotification:ON_REPLAY_FAILURE withObject:theObject];
}
//The onSynchronize method overrides the
//onSynchronize method from SUPDefaultCallbackHandler.
- (SUPSynchronizationActionType)onSynchronize:(SUPObjectList *)syncGroupList withContext:(SUPSynchronizationContext *)context
{
        NSString *notification = nil;
    switch ([context status]) {
        case SUPSynchronizationStatus_STARTING:
            // perform necessary action
            break;
        case SUPSynchronizationStatus_ERROR:
            notification = ON_SYNCHRONISATION_FAILURE;
            break;
        case SUPSynchronizationStatus_FINISHING:
            notification = ON_SYNCHRONISATION_SUCCESS;
            break;
        case SUPSynchronizationStatus_ASYNC_REPLAY_UPLOADED:
            // perform necessary action
            break;
        case SUPSynchronizationStatus_ASYNC_REPLAY_COMPLETED:
            // perform necessary action
            break;
        case SUPSynchronizationStatus_DOWNLOADING:
            // perform necessary action
            break;
        case SUPSynchronizationStatus_STARTING_ON_NOTIFICATION:
            // perform necessary action
            break;
        case SUPSynchronizationStatus_UPLOADING:
            // perform necessary action
            break;
        default:
            break;
    }
     if (notification != nil)
         [self postNotification:notification withObject:nil];
    return SUPSynchronizationAction_CONTINUE;
}
-(void) onGetSyncStatusChange:(SUPSyncStatusInfo*)info
{
    switch(info.state)
    {    
        case SYNC_STATE_NONE:
            MyLog(@"SYNC_STATE_NONE");
            break;
        case SYNC_STATE_STARTING:
            MyLog(@"SYNC_STATE_STARTING");
            break;
        case SYNC_STATE_CONNECTING:
            MyLog(@"SYNC_STATE_CONNECTING");
            break;
        case SYNC_STATE_SENDING_HEADER:
            MyLog(@"SYNC_STATE_SENDING_HEADER");
            break;
        case SYNC_STATE_SENDING_TABLE:
            MyLog(@"SYNC_STATE_SENDING_TABLE");
            break;
        case SYNC_STATE_SENDING_DATA:
            MyLog(@"SYNC_STATE_SENDING_DATA");
            break;
        case SYNC_STATE_FINISHING_UPLOAD:
            MyLog(@"SYNC_STATE_FINISHING_UPLOAD");
            break;
        case SYNC_STATE_RECEIVING_UPLOAD_ACK:
            MyLog(@"SYNC_STATE_RECEIVING_UPLOAD_ACK");
            break;
        case SYNC_STATE_RECEIVING_TABLE:
            MyLog(@"SYNC_STATE_RECEIVING_TABLE");
            break;
        case SYNC_STATE_RECEIVING_DATA:
            MyLog(@"SYNC_STATE_RECEIVING_DATA");
            break;
        case SYNC_STATE_COMMITTING_DOWNLOAD:
            MyLog(@"SYNC_STATE_COMMITTING_DOWNLOAD");
            break;
        case SYNC_STATE_SENDING_DOWNLOAD_ACK:
            MyLog(@"SYNC_STATE_SENDING_DOWNLOAD_ACK");
            break;
        case SYNC_STATE_DISCONNECTING:
            MyLog(@"SYNC_STATE_DISCONNECTING");
            break;
        case SYNC_STATE_DONE:
            MyLog(@"SYNC_STATE_DONE");
            //[self postNotification:ON_SYNC_STATE_DONE withObject:nil];
            break;          
        case SYNC_STATE_ERROR:
            MyLog(@"SYNC_STATE_ERROR");
            break;
        case SYNC_STATE_ROLLING_BACK_DOWNLOAD:
            MyLog(@"SYNC_STATE_ROLLING_BACK_DOWNLOAD");
            break;
        case SYNC_STATE_UNKNOWN:
            MyLog(@"SYNC_STATE_UNKNOWN");
            break;
        default:
            MyLog(@"DEFAULT");
            break;      
    }
}

Step 2 : Create a plist file for storing the settings of relay and SUP server properties.

Step 3 : Create a separate class for SUP application registrtation and connection process and read from plist file as below. Check whether required settings are placed in plist file.

NOTE : WE do a AUTO Registration here

//Preprocessor Directives (//same as you given in plist key)
#define kRelayServer @"Relay Server" 
#define kRelayServerPort @"Relay Server Port"
#define kFarmID @"Farm ID" 
#define KURLSuffix @"URL Suffix"       
#define KNetworkStreamParams @"NetworkStreamParams"
#define kManualRegistration @"Manual Registration"
@synthesize RelayServerName, RelayServerPort, SUPFarmID, SUPURLSuffix,SUPNetworkStreamParams;
-(NSDictionary *)readFromPlist:(NSString*)plistName
{
    NSString *errorDesc = nil;
    NSPropertyListFormat format;
    NSString *docPath;
    NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                              NSUserDomainMask, YES) objectAtIndex:0];
    docPath = [rootPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", plistName]];
    NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:docPath];
    NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
                                          propertyListFromData:plistXML
                                          mutabilityOption:NSPropertyListMutableContainersAndLeaves
                                          format:&format
                                          errorDescription:&errorDesc];
    if (!temp) {
        NSLog(@"Error reading plist: %@, format: %d", errorDesc, format);
    }
    NSLog(@"dict read array::%@",temp);
    NSLog(@"plist name : %@",plistName);
    return temp;
}
- (BOOL)testForRequiredSettingsOfPlist:(NSString*)plistName
{
    MyLog(@"plist:%@",plistName);
    [self createPlistWithFileName:plistName];
    NSDictionary *dict = [self readFromPlist:plistName];
    self.RelayServerName = [dict objectForKey:kRelayServer];
    self.RelayServerPort = [dict objectForKey:kRelayServerPort];
    self.SUPFarmID = [dict objectForKey:kFarmID];
    self.SUPURLSuffix = [dict objectForKey:KURLSuffix];
    self.SUPNetworkStreamParams = [dict objectForKey:KNetworkStreamParams];
    self.SUPManualRegistration = [[dict objectForKey:kManualRegistration] boolValue];
    if(self.SUPServerName == nil ||
       self.SUPUserName == nil ||
       self.SUPFarmID == nil)
    {
        [self showNoTransportAlert:kSUPErrorFailure];
        return NO;
    }
    return YES;
}

TIPS  :You have to remember one point that even though SUP 2.1.3 provides RBS mechanism in ios or Android, application registration and connection process is still MBS.

Step 4 : create a method to set your application identifier:

-(void)setAppIdentifier{
    SUPApplication* app = [SUPApplication getInstance];
  if([SUPApplication messageClientStatus]== MC_STATUS_NOT_INITIALIZED)
//Basically set your application identifier for very first time only.You can do that by checking your message client status (code for "not initialized" should be -1)
{
        app.applicationIdentifier = @"Sample";
}
}

TIPS : You don’t want to explicitly create database because any SUP – ios API interaction would create that for you.

Step 5 : Set the application connection properties for Registration through Relay server

-(void)settingApplicationConnection
{
    SUPApplication* app = [SUPApplication getInstance];
    SUPCallbackHandler *ch = [SUPCallbackHandler getInstance];
    [self setCallbackHandler:ch];
    [app setApplicationCallback:[self callbackHandler]];
    [SampleDBSampleDB registerCallbackHandler:self.callbackHandler];
    //Below properties are for registering through relay server
    SUPConnectionProperties* props = app.connectionProperties;
    [props setServerName:RelayServerName];
    [props setPortNumber:[RelayServerPort intValue]];
    [props setUrlSuffix:SUPURLSuffix];
    [props setFarmId:SUPFarmID];
    SUPLoginCredentials* login = [SUPLoginCredentials getInstance];
        login.username = strUsername; // your Login user name
        login.password = strPassword;  // Your Login password
        props.loginCredentials = login;
}

TIPS : If you have multiple user login scenarios,you don’t need to delete the database if you have kept “cache policy on demand” in your  SUP project.Everytime you login in with different users,your local Database is kept in sync with CDB for the particular user.

TIPS : Previous SUP version provided an SQL lite database in your ios application .SUP 2.1.3 version provides you an Ultralite Database .

Step 6 : Set the application connection properties for Registration through Relay server

-(void)SettingDBForConnectionProfile
{
   SUPApplication* app = [SUPApplication getInstance];
    SUPConnectionProfile *cp = [SampleDBSampleDB getConnectionProfile];
    [cp.syncProfile setDomainName:@"Default"];
    [cp enableTrace:NO];
    [cp.syncProfile enableTrace:NO];
    [SampleDBSampleDB setApplication:app];
}

TIPS : An Auto created database would hold an auto generated encryption key.Thereby if you want to open the database manually by any tool,you have to explicitly use “create database” and set your own encryption key

Step 7 : Register or Start the connection . If you have SSO enabled and SAP password lock policy (no of attempts),you should set “CONNECTION_TIME_OUT_LIMIT”  to “0”.  otherwise timeout duration(say 30)  would try attempting wrong passwords(if provided) and lock your account even on one try.Please be careful on this.

-(void)RegisterOrStartConnection
{
    SUPApplication* app = [SUPApplication getInstance];
    if (![app isRegistered]){
        // If the application has not been registered to the server,
        // register now
        [app registerApplication:CONNECTION_TIME_OUT_LIMIT];
    }
    else{   
        // start the connection to server
        [app startConnection:CONNECTION_TIME_OUT_LIMIT];
    }
}

Step 8 :  Create a  method “InitializeSUP ” which has to be called in your appdelegate or loginviewcontroller class(depending upon your UI).

Note : Application Sequence goes as below

    1) Set app identifier

    2) Set app connection properties

    3) Set DB for connection profile

    4) Register or start connection

    5) Set RBS synchronization properties

    6) Set  Personalization parameters for Authentication purpose

    7) Call synchronize

- (void)initializeSUP
{
    @try {
        [self setAppIdentifier];
        [self settingApplicationConnection];
        [self SettingDBForConnectionProfile];
        [self RegisterOrStartConnection];
        SUPConnectionProfile *sp = [SampleDBSampleDB getSynchronizationProfile];
        [sp setAsyncReplay:NO];  //If you want to do asynchronous replay ,you can set to YES.
        [sp setServerName:RelayServerName];
        [sp setNetworkProtocol:@"http"];
        [sp setPortNumber:80];
        [sp setNetworkStreamParams:SUPNetworkStreamParams];
        [sp setUser:strUsername];   //Login User name
        [sp setPassword:strPassword]; //Login password
        SamplePersonalizationParameters *personalisationParam = [SampleDBSampleDB getPersonalizationParameters];
        [personalisationParam setUsername:strUsername]; //Login User name
        [personalisationParam setPassword:strPassword]; //Login password
        [personalisationParam save];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(syncSuccess) name:ON_SYNCHRONISATION_SUCCESS object:nil];
        [SampleDBSampleDB synchronize];
    }
    @catch(SUPPersistenceException *pe){
        if ([[pe message] rangeOfString:@"loginFail,Sync failed"].location == NSNotFound) {
           [self showNoTransportAlert:@"An error occurred when attempting to log in."];
        } else {
           [self showNoTransportAlert:@"Incorrect username or password."];
        }
        return;
    }
    @catch (SUPSynchronizeException *se) {
        [self showNoTransportAlert:@"Sync Error"];
        return;
    }
    @catch (SUPConnectionPropertyException *cpe) {
        [self showNoTransportAlert:@"Error in connection"];
        return;
    }
    @catch (SUPApplicationRuntimeException *re) {
            [self showNoTransportAlert:@"Incorrect username or password."];
        return;
    }
    @finally {
//Write  code for datavault lock if you have one
   }
}

Step 9 :  Load your UI view on Synchronization success fired by your call back handler.

-(void)syncSuccess
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:ON_SYNCHRONISATION_SUCCESS object:nil];
    NSNotification *n = [NSNotification notificationWithName:LOAD_NEXT_VIEW object:nil];
    [[NSNotificationCenter defaultCenter] postNotification:n];     
}

Step 10 : Run  your app, it just works!

Sys Environment:

SUP: 2.1.3; ERP: ECC 6.0; iOS: 5.1.1 {iPad & iPhone}; Technique: RBS.

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Derek Wichmann

    Hi Vijay,

    Can you describe how the relay server and application connection template is configured in SCC, in particular I’m unclear about the URL Suffix.  I see that in your example you are setting the url suffix in both the connection properties for registration and the connection profile for synchronization via the stream params.  And for registration it needs to be the MBS farm, and sync needs to be the RBS farm.

    I’m unclear if this needs to match exactly (including farmId suffix) to the relay server config and connection template config.

    I keep getting a mobilink error and I think it might have something to do with this:

    2012-09-27 14:48:56.773 DawnROE[34988:15203] [ERROR] [MBODataManager.m:134] SUPPersistenceException: SUPPersistenceException from synchronize: — SUPSynchronizeException: Sync failed: -1305 (MOBILINK_COMMUNICATIONS_ERROR) %1:63 %2: %3:51Details:

    StreamErrorCode = 63

    StreamErrorMessage =

    (0) 
  2. john mabassa

    Hi Vijay,

    This is one of the important document that I was looking for, basically this application sequence one of the most important component of a native SUP app. Even the SUP101 doesnt clearly articulate why many of the codes inside the subscription class is written. Thanks for writing this. Can you also put some insights on the code inside CallBackHandler…all those replay success/ subscribe success and all, on how those works and if possible on the below scenarios

    1. How to handle an interruption during the initial sync?

    2. How to roll back if subscription or sync fails?

    3. How to handle sync errors?

    If there already a doc exists please point me to that

    -anoop

    (0) 

Leave a Reply