IOS Integration How To's

From Pengower
Jump to: navigation, search

Introduction

iOS is the world’s most advanced mobile operating system, continually redefining what people can do with a mobile device. Together, the iOS SDK and Xcode IDE make it easy for developers to create revolutionary mobile apps. iOS can be integrated with the Pengower Platform, data can be sent and received using web services API, this methodology is also App Store compliant, which enables you to create customised iOS Apps for your organization available in the App Store.

Communication with the Platform

We recommend the use of wsdl2objc as the tool to use in order to connect with the Web Service API. This generates Objective-C (Cocoa) code from a WSDL for calling SOAP services, obviously there are many other tools out there to achieve the same so feel free to use the one of your choice. You might want to have a look now at our Web Services API article for further information regarding this integration process. For instance, the login method for the Web Service API will look similar to the following in Objective-C (Cocoa):

//Returns a login token

+(NSString *)loginClient:(NSString *)client aplication:(NSString *)aplication user:(NSString *)user password:(NSString *)password {

   NSString *result = @"";
   
   @try
   {
       penAPISoap12Binding *binding = [self connectpenAPI];
       
       penAPI_Login *request = [[penAPI_Login alloc] init];
       
       request.client = client;
       request.application=aplication;
       request.username=user;
       request.password=password;
       
       //Web Service response
       penAPISoap12BindingResponse *response = [binding LoginUsingParameters:request];
       
       for(id mine in response.bodyParts)
       {
           if([mine isKindOfClass:[penAPI_LoginResponse class]])
           {
               result = [mine LoginResult];
           }
       }
   }
   @catch (NSException *exception)
   {
       NSLog(@"Login Exception: %@", exception);
   }
   @finally
   {
       
   }
   return result;
   }


Wsdl2objc will provide you with the necessary tools to achieve this. If you need further information regarding formatting and expected outputs you can read the asmx information in wsdl format in the url: http://www.penapplications.com/the_name_of_your_configuration/penApi.asmx

If you need to send parameters to the Pengower Platform, the behaviour is the same as in .NET, you can create a class named ActionParam.m that will handle this task of parameters creation. In order to embed the parameters in the SOAP Request that the parser will build for you, you can convert the parameters xml content into Base64-encoded, the content will not conflict with the rest of the SOAP package and it will be decoded on the server side appropriately as a standalone xml document. Arrays must be enclosed within an <elements> tag, that will contain inside the elements of the array, nested arrays are also possible.

Parsing the response

Another important task is to parse the SOAP response from the server, this will contain the required information but obviously it will be inside a SOAP package, in XML format. We recommend the use of the library <libxml/xmlreader.h> as a method for parsing the response. This parser is of SAX type, we strongly recommend this over DOM type parsers, the reason behind it is that loading the whole response in memory can eat up much of your memory available in your mobile device, this might not be the case with the new generation of devices, however if performance issues arise, definitely SAX parsing, in conjunction with parsing callbacks, will be more suitable. SAX parsing can become tricky to code, nevertheless we provide you with a SAX Parsing Template method, that will help you achieving this task:

//Parses an XML string, it retrieves its output +(NSMutableArray *)parseOutputXML:(NSString *)xml

{
   //NSData is a raw stream of bytes
   NSData* xmlData = [xml dataUsingEncoding:NSUTF8StringEncoding];
   xmlTextReaderPtr reader = xmlReaderForMemory([xmlData bytes],
                                                [xmlData length],
                                                nil, nil,
                                                (XML_PARSE_NOBLANKS | XML_PARSE_NOCDATA | XML_PARSE_NOERROR | XML_PARSE_NOWARNING));
   Stack *output = nil;
   if (reader)
   {
       NSDictionary *currentDocument = nil;
       NSString *currentTagName = nil;
       NSString *currentTagValue = nil;
       char *temp = nil;
       bool jumpEndTag = false;
       
       while (true)
       {
           if (!xmlTextReaderRead(reader)) break;
           switch (xmlTextReaderNodeType(reader))
           {
               case XML_READER_TYPE_ELEMENT:
                   //We are starting an element tag
                   temp =  (char*)xmlTextReaderConstName(reader);
                   currentTagName = [NSString stringWithCString:temp
                                                       encoding:NSUTF8StringEncoding];
                   if ([currentTagName isEqualToString:returnTag])
                   {
                       currentDocument = [NSMutableDictionary dictionary]; 
                   }
                   else if ([currentTagName isEqualToString:outputTag])
                   {
                       output = [[Stack alloc] init];
                   }
                   else if ([currentTagName isEqualToString:elementsTag]) 
                   //Every "elements" tag is a new array to be stored on top of the stack, each element in the stack depicts a depth level in the array structure.
                   {
                       NSMutableArray *newElementsArray = [[NSMutableArray alloc] init]; 
                       //It is declared locally so that we still keep track of all the outer arrays, otherwise they will get overriden.
                       [output push:newElementsArray];
                   }
                   continue;
               case XML_READER_TYPE_TEXT:
                   //The current tag has a text value, stick it into the current node
                   temp = (char*)xmlTextReaderConstValue(reader);
                   currentTagValue = [NSString stringWithCString:temp
                                                        encoding:NSUTF8StringEncoding];
                   if (!currentDocument) break;
                   if (output != nil)
                   {
                       [output storeElement:currentTagValue]; //Stores the text on the array that is on top of the stack
                       jumpEndTag = true; 
                       //It jumps one end tag "</ele>", if there is a next "<ele>" then this will be true, if not, that means an "</elements>" is coming,
                       //so we need to pop the array from the Stack in default:
                   }
                   else
                   {
                       [currentDocument setValue:currentTagValue forKey:currentTagName]; 
                       //We don't really use/need the dictionary, in the end we just return the output. The "Output" key never gets stored in the dictionary.
                   }
                   currentTagValue = nil;
                   currentTagName = nil;
                   continue;
               default: //Empty text and closing tags
                   if (!jumpEndTag)
                   {
                       [output pop];
                   }
                   else
                   {
                       jumpEndTag = false;
                   }
                   continue;
           }
       }
   }
   return [output array];
}

The approach behind handling nested arrays is the use of the abstract data type Stack. When the reading of the inner array has been finished, then the parser pops it out of the stack and insert it into its outer array, hence this outer array is now on top of the stack. You can create your own Stack class in Objective-C to help you with this task.

Good practices

Consider the following best practices when implementing iOS Apps that communicate with the Pengower Platform:

1. Load up the information you need when launching the app, this way the user will wait for the data to download just once, not in every screen transition.

2. Use an UINavigationBar to navigate conveniently between your application screens.

3. Encode pictures or any other data files in Base64. This is to ensure that the data remain intact without modification during transport. Base64 is commonly used in a number of applications including email via MIME, and storing complex data in XML.

4. Due to security reasons, do not store any personal or systems information in the NSUserDefaults of your application bundle, users with a jailbroken device will be able to read these data. Set your default Web Service API url credentials hardcoded in your implementation file.

5. Design your application as dynamic as possible, reviews in the App Store can take up to 10 days to get approved, so try to change as much as you can of your application from your server, this way you will not need to re-submit your app for further revision every time a change needs to be made.

6. Make your user informed all the time. Use activity indicators whenever a long process is taking place, for example downloading data from your server, show alerts with the result of the process, make sure you parse the output appropriately to detect success or errors in your Web Service API responses.

7. Your app will need to be connected to the Internet in order to be able to access the Pengower Platform, make sure you are connected through WiFi or 3G in order to access your server, make your user informed if the app can't connect to the Pengower Platform because an Internet connection is not available. We recommend to show an alert informing of this scenario to the user, the app should not crash or end because of Internet connection availability.