IBM i > DEVELOPER > RPG

Build an App to Access IBM i Data From Your iPhone

Access IBM i Data From Your iPhone

As it becomes more and more important (and necessary) to stay connected, it’s equally important to provide people with the tools they need. These days, we expect to be able to get information at any time. In addition, we need to respond quickly if an issue arises. Sure, we could power up the laptop and sign on to the IBM i to get what we need, but what if we could take care of business right from our phone? It’s true that it may only save a few minutes, but sometimes minutes count. You can read more about why mobile access can make a difference for businesses here

Think of the possibilities here; wouldn’t it be great to check the latest sales figures or the status of an order from your phone? Perhaps you want to see how your sales people are performing. How about checking performance statistics of your IBM i? Can I really do this all from my phone? The answer is yes and providing this type of information is much easier than you may think. In fact, you probably already have some of what you need. All that’s required is an RPG program to retrieve data, a web service to access the data, and an app to present it. I’d be willing to bet you already have RPG programs built to provide important metrics to your business.

You may or may not have web services created over them, but a tool like Integrated Web Service (IWS) makes this really easy. What’s more, it’s likely that IWS is already available on your IBM i. So, with an RPG program and web service in place, the only remaining task is to create an app to consume a web service. The app can be built on any platform or device. It can be a phone or tablet; Apple, Android, or Windows; it really doesn’t matter and that’s the beauty of this approach. For this article, I built an iPhone app, but the concept would be the same for other devices and platforms.

I chose to build the app on an iPhone using XCode as an IDE. XCode happens to be the IDE of my choice, although there are others (AppCode, Xamarin, etc.) that could be used as well. The app I built provides information about items in an order entry system and allows one to browse item classes and drill down into a particular item to view its details. It’s a pretty simple example, but it’s perfect to get our feet wet on this project.

I mentioned there are three pieces to this project: an RPG program, a web service, and an app. I’ll start with the RPG program since this is something you’re likely familiar with, and it will give an idea of what I’m looking to accomplish. I created an RPG module that contains three procedures and built it as a service program. I chose build it as a module versus a program as I wanted to have multiple procedures that perform different (but related) tasks. Why three procedures? I wanted to have a separate procedure for each function of the app. One procedure to present the item classes (Figure 1), one to present items within a class (Figure 2), and another to show the details of an item (Figure 3). Building it this way allows me to create one web service instead of multiple services. Of course, you could choose to build separate services to represent each of the functions provided in the app. It’s just a matter of personal preference.

Figure 1. Item class procedure

// - - - -
// Retrieve item classes procedure

dcl-proc retrieveItemClasses export;

   dcl-pi *n;

       // this procedure will return all item classes from the item master.
       // limit it to 50 item classes for this example.

       numberOfItemClasses int(10);
       itemClasses likeds(itemClass_Ds) dim(50);

   end-pi;  

Exec sql
      declare itemClassCsr cursor for
        select distinct a.itemClass, b.itemClassDescription
          from       itemMaster a
          inner join itemClass  b
          on a.itemClass = b.itemClass

          group by a.itemClass, b.itemClassDescription
          order by a.itemClass, b.itemClassDescription;

    Exec sql
      open itemClassCsr;

    Exec sql
      fetch itemClassCsr for 50 rows into :itemClasses;

    Exec sql
      get diagnostics :numberOfItemClasses = row_count;

    Exec sql
      close itemClassCsr;      

The Item class procedure simply returns all available item classes from the item master table as indicated by the ‘itemClasses’ data structure. A row count is also sent back to the consumer. For this project, I chose to limit the number of item classes returned to 50.

Figure 2. Item procedure

// - - - -
// Retrieve items procedure

dcl-proc retrieveItems export;

   dcl-pi *n;

       itemClassIn char(2);

       // this procedure will return all items from the item master.  limit
       // it to 200 items for this example.

       numberOfItems int(10);
       itemNumbers   likeds(itemNumber_Ds) dim(200);

   end-pi;      

This procedure accepts an item class as a parameter and returns a list of items that belong to the selected item class. I’m not showing the SQL to populate the data structure as it is very similar to the SQL used in the item class procedure.

Figure 3. Item details procedure

// - - - -
// Retrieve item details procedure

dcl-proc retrieveItemDetails export;

   dcl-pi *n;

       itemClassIn  char(2);
       itemNumberIn char(20);

       // this procedure will return item details from the item master based on the
       // item class/item number sent in.

       itemNumberDetails likeds(itemNumberDetails_Ds);

   end-pi;    

The third and final procedure takes an item class and item number as parameters and returns details about an item.

Although I only show portions of the code here, rest assured, the full code for the module is available for download via the reference tab.

Before I move on to the iPhone app, I want to give a quick glimpse of what the web service looks like in IWS (Figure 4). I won’t get into how to create the web service as there is quite a bit of information already available on that topic, but I thought it would be helpful to see the service visually.

Figure 4. Item master web service in IWS

figure_4.jpg

There are three methods (procedures) defined and each of them returns data in a JSON object.

I showed the RPG program and the web service, so now it’s time to see how I use these in an app. I’ll start by showing the UI, then I’ll go through some of the code. When I start the app, it presents a table that contains a listing of all item classes available (Figure 5). This is done by consuming the ‘retrieveItemClasses’ procedure of the RPG web service. In this exercise, I grab all item classes, but in a production app, you may choose to add some filters here.

Figure 5. Item class UI

figure_5-(1).jpg

Selecting an item class (in this case, item class 63), consumes the service again, this time using the ‘retrieveItems’ procedure to present the next UI (Figure 6) that shows all items within the class.

Figure 6. Items UI

figure_6.jpg

Finally, selecting an item (item number VEL104) once again consumes the web service to display details about the item (Figure 7). So, which procedure was invoked this time? You guessed it: It was the ‘retrieveItemDetails’ procedure.

Figure 7. Item details UI
Figure_7.jpg

Pretty cool, right? For each action I take in the app, the RPG service is consumed, returning the data that was requested.

Before looking at the code that made this happen, I’ll give a little background about building the app. As I mentioned earlier, I used XCode to do all the UI and programming tasks, and the language I used is Objective C. Objective C may look similar to the C# and C programming languages to those familiar with them.

There’s one more thing that’s important to show before digging into the code, and that’s the JSON data that is returned from the web service (Figure 8). Being able to see that actual data will help in understanding the Objective C code.

Figure 8. JSON object for item classes

figure_8.jpg

The JSON object contains the number of item classes returned and an array of dictionaries. Let me explain what that means. With JSON, arrays are enclosed with brackets, while dictionaries are enclosed with curly braces. This is very important to remember since you need to how to parse the JSON in your program.

When the item classes are presented, a method is called to consume the service (Figure 9), load the results into an object (Figure 10), then present them in a table. I’ll show the entire method and break it into logical ‘steps’ so I can explain what each piece is doing.

I start by defining and initializing an array (Figure 9, Step 1) that will hold all the information coming back from the web service. Next, I set up all the configurations I need to make the web service call (Figure 9, Step 2). I build the URL I took from IWS, specify that the service is returning JSON in the HTTP Header, and indicate that I’m using ‘GET’ as the HTTP method. Now, I use ‘NSURLSessionDataTask‘ to initiate the call to the web service (Figure 9, Step 2). When I do this, I receive a response from the server, as well as, if there was an error with the request. If everything goes well, I load the JSON data returned from the service into a dictionary where I can prepare it for use in the UI (Figure 9, Step 4).

So, in the JSON I showed previously, I have an array called ‘itemClasses’ that has dictionaries within it containing an item class (‘itemClass’) and an item class description (‘itemClassDesc’). The goal is to extract all the item classes and their corresponding descriptions from the ‘itemClasses’ array.

I loop through the array and load all the dictionary elements into the ‘ItemClassInfo’ object. Finally, I load the ‘ItemClassInfo’ object into the ‘_itemClasses’ array. I do this because that’s what is being passed back to the class that is presenting the data on the UI (Figure 9, Step 5).

Figure 9. ‘downloadItemClasses’ method

@interface getItemClassModel()

{
    NSMutableArray *_itemClasses;
}

@end

@implementation getItemClassModel

- (void)downloadItemClasses
{
    // all item classes retrieved from the web service will be loaded into this array
    
    // Step 1.  all item classes retrieved from the web service will be loaded into this array

    _itemClasses = [[NSMutableArray alloc] init];

    // Step 2.  set up url configuration

    
    // set up url configuration
    
    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
    
    sessionConfiguration.HTTPAdditionalHeaders = @{@"Current-Type" : @"application/x-www-form-urlencoded",
                                                   @"Accept" : @"application/json"};
    
    NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
    
    // construct the url along with the parameter 'iten class' being sent
    
    NSString *strUrl = @"http://YourUrl:YourPort/web/services/RTV_ITEMS/";
    
    NSURL *url = [NSURL URLWithString:strUrl];
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    // GET differs from POST in that you don't need to send content length and http body with GET
    
    request.HTTPMethod = @"GET";

// Step 3.  this section will initiate the consumption of the web service
    
    // this section will initiate the consumption of the web service
    
    NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            // get reponse
            {
                if (!error)
                {
                    NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
                                                      
                    // check if the response is good
                    
                    if (httpResp.statusCode == 200)
                    {
                        NSError *jsonError;
                                                          
                        NSDictionary *statusJSON = [NSJSONSerialization JSONObjectWithData:data
                                                                                   options:NSJSONReadingAllowFragments
                                                                                     error:&jsonError];
                        if (!jsonError)
                        {
                            // grab the item class list being passed from the web service
                            
                            NSArray *itemClassesArray = [statusJSON objectForKey:@"itemClasses"];
                            
                            // NOTE: the JSON structure being returned from this service is an array, so
                            //       we have to parse it as such.
                            //       Dictionaries are indicated by being enclosed with curly braces {}
                            //       Arrays are indicated by being enclosed with brackets []
                             
                         // Step 4.  grab the item class list being passed from the web service     
                            
                            for (NSDictionary *theStatus in itemClassesArray)
                            {
                                // Create a new ItemClassInfo object and set its props to JsonElement properties
                                
                                getItemClassInfo *newItemClassInfo = [[getItemClassInfo alloc] init];
                
                                newItemClassInfo.strItemClass = theStatus[@"itemClass"];
                                newItemClassInfo.strItemClassDescription = theStatus[@"itemClassDesc"];
                                newItemClassInfo.WSResponseMessage = @"Successful connection to server";
                                                                  
                                // Add this to the itemClasses array
                                
                                [self->_itemClasses addObject:newItemClassInfo];
                            }
                        }
                    }
                    else
                    {
                        // bad response
                        
                        getItemClassInfo *newItemClassInfo = [[getItemClassInfo alloc] init];
                                                          
                        newItemClassInfo.WSResponseMessage = @"Can't connect to server";
                        [self->_itemClasses addObject:newItemClassInfo];
                    }
                }
            }
             
            // Step 5.  Execute the method to present the data on the UI.  

            [self.delegate getItemClassWS:self->_itemClasses];
                                              
        }];
    
    [postDataTask resume];
}

@end

Figure 10. ‘getItemClassInfo’ object

@interface getItemInfo : NSObject

@property (nonatomic, strong) NSString *strItemNumber;
@property (nonatomic, strong) NSString *strItemNumberDescription;
@property (nonatomic, strong) NSString *WSResponseMessage;

@end

So, I have all the item classes; now I present them on the UI via the ‘getItemClassWs’ method (Figure 11).

Figure 11. Load item classes to the UI

-(void)getItemClassWS:(NSArray *)ItemClassesDownloaded
{
    // This delegate method will get called when the items are finished downloading
    
    // Set the downloaded items to the array
    
    downloadedItemClasses = ItemClassesDownloaded;
    
    // i will only populate the response if:
    //
    // 1.  you couldn't connect to the service
    // 2.  there are actual items to download
    
    if (downloadedItemClasses == nil || [downloadedItemClasses count] == 0)
    {
        
    }
    else
    {
        arrWSResponseMessage = [downloadedItemClasses valueForKey:@"WSResponseMessage"];
        strWSResponseMessage = [arrWSResponseMessage objectAtIndex:0];
    }
    
    if ([strWSResponseMessage isEqualToString:@"Can't connect to server"])
    {
        // after calling web service, you need to call dispatch_async on anything UI related otherwise it hangs
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            // if you get a bad response from the server, put up an alert
            
            [self connectionToServerAlert];
            
            [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
            
        });
    }
    else
    {
        // after calling web service, you need to call dispatc_async on anything UI related otherwise it hangs
        
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView reloadData];
            
            [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
        });
    }
}

This method is called upon completion of the consumption of the web service. It accepts an array (the item class array) as a parameter and loads it to the UI. If there are any issues connecting to the server, an alert is displayed to notify the user. That’s it! I now have an app that pulls data from the IBM i. The other methods to display the items and item details are almost identical to the item class method and aren’t shown here, but they can be downloaded.

This article showed how to deliver data from the IBM i to the iPhone using an RPG program, a web service, and an app to display the data. The app I built is fairly simple, but it demonstrates how easy it is to get an app up and running. I’m sure you already have ideas for some apps of your own! Visit this Github page to find the code needed to do this yourself. 

Mike Larsen is a senior IBM i programmer and project manager for Central Park Data Systems.



Like what you just read? To receive technical tips and articles directly in your inbox twice per month, sign up for the EXTRA e-newsletter here.


comments powered by Disqus

Advertisement

Advertisement

2018 Solutions Edition

A Comprehensive Online Buyer's Guide to Solutions, Services and Education.

New and Improved XML-INTO

Namespace support makes the opcode a viable option

Authenticating on the Web

The finer points of OpenRPGUI, Part 1

The Microphone is Open

Add your voice: Should IBM i include open-source RPG tools?

IBM Systems Magazine Subscribe Box Read Now Link Subscribe Now Link iPad App Google Play Store
IBMi News Sign Up Today! Past News Letters