Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

       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-cu...

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.

2 Comments
Labels in this area