Skip to Content
Author's profile photo Former Member

SMP 3.0 OData CRUD Operations in iOS Native

Hi Everyone,

Recently i gone through OData Read, Create, Update and Delete operations in SMP 3.0 Native ODataSDK.

here is a sample code for a OData service


http://sapes1.sapdevcenter.com:8080/sap/opu/odata/IWFND/RMTSAMPLEFLIGHT/TravelagencyCollection

GET Request Format

[RequestBuilder setRequestType:HTTPRequestType];

  

// Enable XCSRF handling for OData requests

[RequestBuilder enableXCSRF:YES];

  

// Initialize the Requesting class with the endpoint URL

id<Requesting> getRequest = [RequestBuilder requestWithURL:[[NSURL alloc] initWithString: appEndPoint]];

  

// Add username and password for the end point

[getRequest setUsername:username];

[getRequest setPassword:password];

   

/// Set our request headers.

[getRequest setRequestMethod:@”GET”];

[getRequest addRequestHeader:@”X-Requested-With” value:@”XmlHttpRequest”];

[getRequest addRequestHeader:@”Content-Type” value:@”application/xml; charset=UTF-8″];

[getRequest addRequestHeader:@”Accept” value:@”application/xml,application/atom+xml”];

  

// This class is the delegate callback for any notifications (success and failure)

[getRequest setDelegate:self];

[getRequest setDidFinishSelector:@selector(handleReqSuccess:)];

[getRequest setDidFailSelector:@selector(handleReqFailed:)];

  

// Each request can be tagged with a number.

getRequest.requestTag = tag; // Useful for a change request.

  

// Start our request.

[getRequest startAsynchronous];


POST Request Format

[RequestBuilder setRequestType:HTTPRequestType];

[RequestBuilder enableXCSRF:YES];

id<Requesting> postRequest = [RequestBuilder requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@”%@/%@”,appEndPoint,collectionName]]];

[postRequest setRequestMethod:@”POST”];

  

[postRequest addRequestHeader:@”Content-Type” value:@”application/json; charset=UTF-8″];

[postRequest addRequestHeader:@”Accept” value:@”*/*”];

// Here cSRFToken is retrieved from the GET Response.

[postRequest addRequestHeader:@”x-csrf-token” value: cSRFToken];

NSMutableData* postData;

// GetEntry is an ODataObject which has the response for GET done on single entry from a collection

ODataEntry const *postBody = [[ODataEntry alloc]initWithEntitySchema:[[[self.serviceDocument getSchema] getCollectionByName:self.entityName] getEntitySchema]];

  

ODataPropertyValueObject* filterValue = [postBody getPropertyValueByPath:@”agencynum”];  ///agencynum is the primary key or the unique value

   

          filterValue.rawValue=@”00000127″;

    

ODataPropertyValueObject* filterValue1 = [postBody getPropertyValueByPath:@”agencyname”];

    

filterValue1.rawValue=@”BestAgency”;

  

//The following code can be used to build a JSON post body:

ODataEntryBody *entryBody = buildODataEntryRequestBody(postBody, ENTRY_OPERATION_CREATE, self.serviceDocument, NO, BUILD_STYLE_JSON);  

      

postData = [[entryBody.body dataUsingEncoding:NSUTF8StringEncoding]mutableCopy];

[postRequest addRequestHeader:@”Content-Length” value:[NSString stringWithFormat:@”%i”,[postData length]]];

[postRequest setPostBody:postData];

[postRequest setDelegate:self];

  

// Each request can be tagged with a number.

          getRequest.requestTag = tag; // Useful for a change request.

// Start our request.

[postRequest startAsynchronous];

PUT Request Format


[RequestBuilder setRequestType:HTTPRequestType];

  

[RequestBuilder enableXCSRF:YES];

  

id<Requesting> putRequest = [RequestBuilder requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@”%@/%@(%@)”,appEndPoint,collectionName,agencynum]]];

  

[putRequest setRequestMethod:@”PUT”];

[putRequest addRequestHeader:@”Content-Type” value:@”application/json; charset=UTF-8″];

  

[putRequest addRequestHeader:@”Accept” value:@”*/*”];

  

[putRequest addRequestHeader:@”x-csrf-token” value:cSRFToken];

  

NSMutableData *postData;

ODataEntry const *postBody = [[ODataEntry alloc]initWithEntitySchema:[[[self.serviceDocument getSchema] getCollectionByName:self.entityName] getEntitySchema]];

ODataPropertyValueObject* filterValue = [postBody getPropertyValueByPath:@”agencyname”];

  

filterValue.rawValue=@”NewAgencyName”;

  

//The following code can be used to build a JSON post body:

ODataEntryBody *entryBody = buildODataEntryRequestBody(postBody, ENTRY_OPERATION_CREATE, self.serviceDocument, NO, BUILD_STYLE_JSON);

      

postData = [[entryBody.body dataUsingEncoding:NSUTF8StringEncoding]mutableCopy];

  

[putRequest addRequestHeader:@”Content-Length” value:[NSString stringWithFormat:@”%i”,[postData length]]];

  

[putRequest setPostBody:postData];

  

[putRequest setDelegate:self];

  

putRequest.requestTag = tag;

  

// Start our request.

  [putRequest startAsynchronous];


DELETE Request Format


[RequestBuilder setRequestType:HTTPRequestType];

  

[RequestBuilder enableXCSRF:YES];

  

id<Requesting> deleteRequest = [RequestBuilder requestWithURL:[NSURL URLWithString:[NSString stringWithFormat@”%@/%@(%@)”,appEndPoint,collectionName,agencynum]]];

  

[deleteRequest setRequestMethod:@”Delete”];

  

[deleteRequest addRequestHeader:@”Content-Type” value:@”application/json; charset=UTF-8″];

  

[deleteRequest addRequestHeader:@”Accept” value:@”*/*”];

  

[deleteRequest addRequestHeader:@”x-csrf-token” value:self.cSRFToken];

  

[deleteRequest setDelegate:self];

  

deleteRequest.requestTag = tag;

  

// Start our request.

[deleteRequest startAsynchronous];


Regards

Siva Chandu

Assigned Tags

      15 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Nice Aticle and useful information..!!

      Author's profile photo Former Member
      Former Member

      Hi Chandu,

      Im able to register the device on smp cockpit .But when im trying to fetch the data ,im getting the following error.Even i used above code for the fetching the data.

      Enabling offline...

      2014-10-10 12:39:45.044 Testsmp3[851:34851] Registering reachability notifier for: db41vv12.home

      2014-10-10 12:39:45.045 Testsmp3[851:34854] Start queue...

      2014-10-10 12:39:45.045 Testsmp3[851:34851] UrlString:   http://db41vv12.home:8080

      2014-10-10 12:39:45.046 Testsmp3[851:34851] INTIALIZE CACHE

      2014-10-10 12:39:45.057 Testsmp3[851:34854] Error Domain=EncryptionKeyError Code=5001 "The operation couldn’t be completed. (EncryptionKeyError error 5001.)"

      2014-10-10 12:39:45.061 Testsmp3[851:34851] Cache Error --> Error Domain=CacheOperationError Code=1000 "Cannot create an NSPersistentStoreCoordinator with a nil model" UserInfo=0x7918e930 {NSLocalizedDescription=Cannot create an NSPersistentStoreCoordinator with a nil model}

      2014-10-10 12:39:45.061 Testsmp3[851:34851] GET DELTA LINK

      2014-10-10 12:39:45.061 Testsmp3[851:34851]    ERROR = Null or Empty encryption key passed

      2014-10-10 12:39:45.063 Testsmp3[851:34851] Error Domain=EncryptionKeyError Code=5001 "The operation couldn’t be completed. (EncryptionKeyError error 5001.)"

      2014-10-10 12:39:45.064 Testsmp3[851:34796] Request Failed

      2014-10-10 12:39:45.064 Testsmp3[851:34851] Connectivity changed...

      2014-10-10 12:39:45.064 Testsmp3[851:34851] Detected network...

      2014-10-10 12:39:45.064 Testsmp3[851:34851] Start queue...

      2014-10-10 12:39:45.065 Testsmp3[851:34851] Error Domain=EncryptionKeyError Code=5001 "The operation couldn’t be completed. (EncryptionKeyError error 5001.)"



      Thanks,

      Praveen

      Author's profile photo Former Member
      Former Member
      Blog Post Author

      Hi Praveen

      is this problem occurs when u go offline ?

      if Yes, i would suggest to check the cache is initialized properly, and setting the Encryption key before initializing Cache.

      Regards,

      Siva Chandu

      Author's profile photo Former Member
      Former Member

      No this occurs when im online.

      Author's profile photo Former Member
      Former Member
      Blog Post Author

      Praveen,

      As the log shows problem with the encription key.

      Please check the format of initializing the Cache as follows,

      cache should be initialized once at the start of application

      NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

          NSString *key= [userDefaults objectForKey:ENCRIPT_KEY];

         

          if (key == nil) {

              key = [EncryptionKeyManager getEncryptionKey:nil];

              [userDefaults setObject:key forKey:ENCRIPT_KEY];

              [userDefaults synchronize];

          }

          else{

              [EncryptionKeyManager setEncryptionKey:key withError:nil];

          }

         

          /***

           Initialize the Cache once for the application lauch.

           ***/

         

          id<Caching>defaultCache = [[Cache alloc] init];

          [defaultCache setIsPersistable:YES];

         

          NSError* error = nil;

         

          if (![defaultCache initializeCacheWithError:&error]) {

              NSLog(@"Initialize Error : %@@", error);

              return;

          }

      this works for me.

      Regards

      Chandu

      Author's profile photo Former Member
      Former Member

      Chandu,

      I trying to fetch the data with the code which is in this link.

      Getting started with SMP3 Native OData iOS apps

      Im not sure were to initialize the nsuerdefaluts ,encryptionkeymanager ,code in the above  link.

      Could you please help me in this.

      Thanks,

      Praveen

      Author's profile photo Former Member
      Former Member
      Blog Post Author

      Are u able to get the service doc and metadata for the service.

      can u provide the code you are using.

      As the same blog i have also tried and is worked fine.

      if you want to use the offline capabilities u have to initialize the cache.

      Regards

      Chandu

      Author's profile photo Former Member
      Former Member

      In the Rest Client im getting the data,but im not getting the service document and metadata.The below is the code.

      Im very new to SMP .Please help me.

      the below code is in the another file which of nsobject.

      - (NSError *)initialiseSMP

      {

         

          NSError *error = nil; // Generic error object.

         

          //ViewController *view = [[ViewController alloc]init];

       

         // AppDelegate *appDelegate= [[UIApplication sharedApplication] delegate];

          // Set username and password

          self.username = XXX;

          self.password = XXX";

         

         

          // Setup system access info.

          self.smpAppId = @"com.testios.demo";

          self.smpSecProvider = @"xxx";

          self.smpServer = @"xxxx";

          self.smpPort = @"xxx";

         

          // Setup odata service info

          self.odataURL = @"http://xxx";

          self.entityName = @"xxx";

         

          // initialise the SMP connection with the information.

          self.clientConn = [SMPClientConnection

                             initializeWithAppID:self.smpAppId

                             domain:@"default"

                             secConfiguration:self.smpSecProvider];

         

          // Setup the connection profile on the connection.

          [self.clientConn setConnectionProfileWithHost:self.smpServer port:self.smpPort farm:nil relayServerUrlTemplate:nil enableHTTP:YES];

              //[cacheLocal setIsPersistable:NO];

         

          // Set this class to be a delegate for any user-registration related notificaitons.

          [SMPUserManager setDelegate:self];

         

         

         

          // Make the user manager aware of the connection.

          SMPUserManager *userManager = [SMPUserManager initializeWithConnection:self.clientConn];

         

          // Finally - register the user.

          [userManager registerUser:self.username password:self.password error:&error isSyncFlag:NO];

         

          // Return any basic errors that have occurred.

          return error;

      }

      #pragma mark - User Manager Delegate calls

      // Callback for when a user registration failed.

      -(void)userRegistrationFailed:userManager

      {

          NSLog(@"Failed to register user.");

          if (self.delegate && [self.delegate respondsToSelector:@selector(userRegistrationSuccessful)]) {

              [self.delegate userRegistrationFailed:@"User already Registerd."];

          }

      }

      // Callback for when a user registration is successful.

      -(void)userRegistrationSuccessful:userManager

      {

          //NSError *error=nil;

         

          NSLog(@"Sucessfully registered user.");

           [EncryptionKeyManager resetEncryptionKey];

        

          if (self.delegate && [self.delegate respondsToSelector:@selector(userRegistrationSuccessful)])

          {

              [self.delegate userRegistrationSuccessful];

          }

      }

      #pragma mark - Generic Request method

      /*

      *  For requesting a specific URL and giving a specific tag number.

      */

      - (void) requestDataForURL:(NSString *)url andTag:(int)tag

      {

         

             [RequestBuilder setRequestType:HTTPRequestType];

         

          // Enable XCSRF handling for OData requests

          [RequestBuilder enableXCSRF:YES];

         

          // Initialize the Requesting class with the endpoint URL

          id<Requesting> serviceDocRequest = [RequestBuilder requestWithURL:[[NSURL alloc] initWithString:url]];

         

          // Add username and password for the end point

          [serviceDocRequest setUsername:@"xxx"];

          [serviceDocRequest setPassword:@"xxx"];

         

          /// Set our request headers.

          [serviceDocRequest setRequestMethod:@"GET"];

          [serviceDocRequest addRequestHeader:@"X-Requested-With" value:@"XmlHttpRequest"];

          [serviceDocRequest addRequestHeader:@"Content-Type" value:@"application/xml; charset=UTF-8"];

          [serviceDocRequest addRequestHeader:@"Accept" value:@"application/xml,application/atom+xml"];

         

         

        

          // This class is the delegate callback for any notifications (success and failure)

          [serviceDocRequest setDelegate:self];

          [serviceDocRequest setDidFinishSelector:@selector(handleReqSuccess:)];

          [serviceDocRequest setDidFailSelector:@selector(handleReqFailed:)];

         

          // Each request can be tagged with a number - useful later.

          serviceDocRequest.requestTag = tag; // Useful for a change request.

         

          // Start our request.

          [serviceDocRequest startAsynchronous];

      }

      #pragma mark - Data Retrieval

      /*

      * Public method to start the data retirval.

      */

      - (void)beginDataRetrieval{

         

          // Set our class to be the offline delegate (not used but needed later)

          [RequestBuilder setDelegate:self];

          [RequestBuilder setDidFinishSelector:@selector(handleReqSuccess:)];

          [RequestBuilder setDidFailSelector:@selector(handleReqFailed:)];

         

          /*

           * In order to retrieve the data you need 3 components:

           * 1) The service document (the top level of the service with the collection list)

           * 2) The metadata information (service/$metadata)

           * 3) The actual data you want (service/collectionName)

           * So we start with number 1.

           */

          [self requestServiceDocument];

      }

      - (void) requestServiceDocument{ // Make a request for the service document

          [self requestDataForURL:self.odataURL andTag:1];

      }

      - (void) requestMetadata{ // Make a request for the metadata of the service.

          [self requestDataForURL:[self.odataURL stringByAppendingString:@"$metadata"] andTag:2];

      }

      - (void) requestData{ // Make a request for the actual data.

          [self requestDataForURL:[self.odataURL stringByAppendingString:self.entityName] andTag:3];

      }

      #pragma mark - request callbacks

      // Generic failure method once a request fails.

      - (void) handleReqFailed:(id)sender{

         

          NSLog(@"Request Failed");

         

          // Tell any listening delegate that there has been a failure.

          if (self.delegate && [self.delegate respondsToSelector:@selector(userRegistrationSuccessful)]) {

              [self.delegate dataRetrievalFailed];

          }

      }

      // Generic success method once a request is complete.

      - (void) handleReqSuccess:(Request *)request{

         

          // Based on which request it is (each request was tagged)

          // Call the correct response parser.

          switch ([request requestTag]) {

              case 1: // This is the service Document

                  [self parseServiceDocumentRequest:request];

                  break;

              case 2: // This is the metadata.

                  [self parseMetadataRequest:request];

                  break;

              case 3: // this is the actual data.

                  [self parseDataRequest:request];

                  break;

              default:

                  break;

          }

          NSLog(@"Request number %i Successful", [request requestTag]);

      }

      #pragma mark - response parsers

      - (void) parseServiceDocumentRequest:(Request *)request{

          //Parse the service document

          ODataServiceDocumentParser *svcDocParser = [[ODataServiceDocumentParser alloc] init];

          NSData *svc = [request responseData];   // Retrieve the raw data.

          [svcDocParser parse: svc];              // Parse the data

           serviceDocument = [svcDocParser getServiceDocument];   // store in a variable.

         

          [self requestMetadata]; // Finally proceed to stage 2 - get the metadata.

      }

      // Parse the metadata.

      - (void) parseMetadataRequest:(Request *)request{

         

          NSData *metadata = [request responseData]; // Retrieve the raw data.

         

          schema1 = parseODataSchemaXML(metadata, serviceDocument); // Parse the data.

         

          [self requestData]; // Now we have 1 and 2 - we can get our data.

      }

      // Parse the data using the service doc and the metadata (AKA the entitySchema).

      - (void) parseDataRequest:(Request *)request{

         

          NSData *entriesData = [request responseData]; // Retrieve the raw data.

         

          // Parse the response by passing in the raw data, the schema of the entity we retrieved and the service doc.

          NSMutableArray *results = parseODataEntries(entriesData,

                                                      [[[serviceDocument getSchema] getCollectionByName:self.entityName] getEntitySchema],

                                                      serviceDocument);

         

          // Now we are done - so we tell the delegate and pass the response back.

          if (self.delegate && [self.delegate respondsToSelector:@selector(userRegistrationSuccessful)]) {

              [self.delegate dataRetrievalComplete:results];

          }

      }

      @end

      The below code is in the view controller wheni click the buttons the register and getdata methods are calling .from there call backs.

      - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

      {

             self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

          if (self) {

              self.controller = [[TESTSMP alloc] init];

              [self.controller setDelegate:self];

          }

          return self;

      }

      - (void)viewDidLoad

      {

           //AppDelegate *appdelegate = [[UIApplication sharedApplication] delegate];

          [super viewDidLoad];

          // Do any additional setup after loading the view, typically from a nib.

         

          button = [UIButton buttonWithType:UIButtonTypeRoundedRect];

         

          button.frame = CGRectMake(50, 60, 150, 30);

         

          [button setTitle:@"Register" forState:UIControlStateNormal];

         

          [button addTarget:self action:@selector(startRegistration:) forControlEvents:UIControlEventTouchUpInside];

         

          [self.view addSubview:button];

         

          resultsTable   = [[UITableView alloc]initWithFrame:CGRectMake(0, 100, 320, 470) style:UITableViewStylePlain];

         

          [self.view addSubview:resultsTable];

         

          getdata = [UIButton buttonWithType:UIButtonTypeRoundedRect];

         

         getdata.frame = CGRectMake(150, 60, 150, 30);

         

          [getdata setTitle:@"Get data" forState:UIControlStateNormal];

         

          [getdata addTarget:self action:@selector(startDataDownload:) forControlEvents:UIControlEventTouchUpInside];

         

          [self.view addSubview:getdata];

         

          resultsTable   = [[UITableView alloc]initWithFrame:CGRectMake(0, 100, 320, 470) style:UITableViewStylePlain];

         

          [self.view addSubview:resultsTable];

         

         

      }

      - (void)viewDidAppear:(BOOL)animated

      {

         

          [self.downloadDataButton setEnabled:NO];

      }

      - (void)displayError:(NSString *)error

      {

          UIAlertView *alertView = [[UIAlertView alloc]

                                    initWithTitle:@"ERROR"

                                    message:error

                                    delegate:nil

                                    cancelButtonTitle:@"OK"

                                    otherButtonTitles:nil

                                    ];

          [alertView show];

      }

      - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

      {

          return 1;

      }

      - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

      {

          // return the number of records we have in the results array.

          return [self.results count];

      }

      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

      {

          // Get a table view cell to work with.

          static NSString *CellIdentifier = @"Cell";

          UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

          if (!cell)

         

          {

              cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];

          }

         

          // Get the entry we currently want to display.

          ODataEntry *entry = [self.results objectAtIndex:[indexPath row]];

          NSMutableDictionary *fields = [entry getFields];

         

          // Retrieve the value for comapny name

          ODataPropertyValueObject *valueObject = [fields objectForKey:@"Validate"];

         

          // Set the value on the cell.

          [cell.textLabel setText:[valueObject getValue]];

         

          // Return the cell complete.

          return cell;

      }

      #pragma mark - SMP delegate methods

      //Callback for when the user-registration was OK.

      - (void)userRegistrationSuccessful

      {

          [self.downloadDataButton setEnabled:YES];

      }

      // Callbcak for when the user-registration failed.

      - (void)userRegistrationFailed:(NSString *)message

      {

          [self displayError:message];

      }

      // Callback for when we have received the data.

      - (void)dataRetrievalComplete:(NSMutableArray *)results

      {

          // Update table here.

          self.results = results;

          [self.resultsTable reloadData];

      }

      // Callback for when the data retrieval failed.

      - (void)dataRetrievalFailed

      {

          [self displayError:@"Unable to retrieve data"];

      }

      #pragma mark - Button press actions

      - (void)startRegistration:(id)sender

      {

         

         

         

          NSError *error = [self.controller initialiseSMP];

         

         

          if (error)

         

          {

              [self displayError:[error debugDescription]];

          }

      }

      - (void)startDataDownload:(id)sender

      {

          [self.controller beginDataRetrieval];

      }

      Author's profile photo Former Member
      Former Member

      Hi, Did you found the soulution ?

      I am also faceing similar issue.

      Thank you

      Preetham Dsouza

      Author's profile photo Former Member
      Former Member

      Hi ,

      Im working on it.

      Thanks

      Praveen Kumar Addiki.

      Author's profile photo Former Member
      Former Member

      Hi ,

      I got the solution .

      I used the MAFlogon for registering the device and SODATAONLINESTRORE class to  READ a record and CREATE record.

      Update the Native sdk to SMP3 SP5 .

      This may help you.

      Thanks ,

      Praveen

      Author's profile photo Former Member
      Former Member

      Hi Chandu,

      How to fetch the Xcsrf token from the get response.

      Can we use the native ios classes to fetch the xcsrf token.Is it a correct way?

      Thanks ,

      Praveen.

      Author's profile photo Former Member
      Former Member

      Chandu ,

      Got the solution .

      i used the below method to fetch the xcsrf token [request responseHeaders].


      Thanks.

      Author's profile photo Former Member
      Former Member
      Blog Post Author

      hi praveen

      i Would like you to take a look at  below link this might help u more in the development.

      http://scn.sap.com/docs/DOC-58677

      Author's profile photo Former Member
      Former Member

      POST Request


      ODataEntryBody *entryBody = buildODataEntryRequestBody(postBody, ENTRY_OPERATION_CREATE,self.serviceDocument, NO, BUILD_STYLE_JSON); 


      I cannot find method buildODataEntryRequestBody . Is this a part of SDK or a custom method ?

      In case it is custom can you please provide the implementation with explanation for all the parameters that are passed ?


      I am on SMP 3.0 SP 07.


      Thanks.