Developer Central
Developer Central Links
iOS Sample
Envirofacts RadNet Representational State Transfer (REST) Application Programming Interface (API)
- Overview
- Requirements
- Create a New Project
- Implement the Data Request
- Define the User Interface
- Run the Application
- Locate More Information on Envirofacts Radiation (RadNet) Data
Overview
This sample will provide the steps to create a simple iOS application that can consume Radiation (RadNet) Data. The Envirofacts RadNet database contains sample results for a variety of
radionuclides and radiation types from 200 monitoring stations across all 50 states and the American Territories. This API Data Sample, queries and displays results of these samples, for an entered analyte.
Following these steps in order will allow the application to recreate the sample project.
Download the complete sample code here: EPA_RADNet.tar.gz.
Requirements
This solution was built using:
- Mac OSX Lion
- xCode 4.2
- xCode is already installed on a working version of Mac OSX.
For this sample, create an iOS project that will consume and list result data for an analyte from the Envirofacts Radiation (RadNet) Web service.
Create a new xCode Project by opening xCode, clicking on "File", then "New", and then "New Project".
Select "Single View Application".
Name the project. For this it is named SampleEpa. Select Device Family "iPhone", check "User Storyboard", and click "Next". Save the files anywhere.
On the iOS Application Target change the Identifier to gov.epa.sample.dfe.SampleEPA where SampleEPA is the project name.
In the Navigator Pane select "MainStoryboard.storyboard" and then in the View Controller Scene "Expand View Controller".
From the Object Library Pane drag and place the following objects on to the view.
- Label - double click on the label and Type "Analyte ID"
- Text Field
- Round Rectangular Button- double click on the button and Type "Query"
- Table View
- Toolbar
On the tool bar, drag:
- Bar Button Item - double click on the button and Type "Prev"
- Flexible Space Bar Button Item
- Bar Button Item - double click on the button and Type "Next"
Click on the Show Assistant Editor (button on the right top part of xcode that looks like a bow tie and shirt) . Make sure on the right side of the split pane the ViewController.h file is selected. Press the Control button and drag the following items to the @end statement in the VIewController.h file .
- Text Field -when prompted name it "AnalyteField"
- Query Button -when prompted name it "QueryButton"
- Table View -when prompted name it "resultTableView"
- Toolbar -when prompted name it "ToolBar"
- Prev Button -when prompted name it "Prev"
- Next Button -when prompted name it "Next"
Now that the Project and the Interface are set up, finish the development. In xCode select the Show the Standard editor button in the upper right corner. In the Navigator pane right click on the "SampleEPA (project name)" folder, then "New File", and then select "Objective-C" class. Name the class AppResultsData and add the following attributes:
// AppResultsData.h
// SampleEPA12
//
// Created by Kevin Dunn on 3/9/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface AppResultsData : NSObject
{
NSString *ANA_NUM;
NSString *RESULT_ID;
NSString *ANALYTE_ID;
NSString *RESULT_AMOUNT;
NSString *CSU;
NSString *MDC;
NSString *RESULT_UNIT;
NSString *RESULT_DATE;
NSString *RESULT_IN_SI;
NSString *CSU_IN_SI;
NSString *MDC_IN_SI;
NSString *SI_UNIT;
}
@property (nonatomic, retain) NSString *ANA_NUM;
@property (nonatomic, retain) NSString *RESULT_ID;
@property (nonatomic, retain) NSString *ANALYTE_ID;
@property (nonatomic, retain) NSString *RESULT_AMOUNT;
@property (nonatomic, retain) NSString *CSU;
@property (nonatomic, retain) NSString *MDC;
@property (nonatomic, retain) NSString *RESULT_UNIT;
@property (nonatomic, retain) NSString *RESULT_DATE;
@property (nonatomic, retain) NSString *RESULT_IN_SI;
@property (nonatomic, retain) NSString *CSU_IN_SI;
@property (nonatomic, retain) NSString *MDC_IN_SI;
@property (nonatomic, retain) NSString *SI_UNIT;
@end
In the implementation class synthesize the attributes and then dealloc them as follows.
// AppResultsData.m
// SampleEPA12
//
// Created by Kevin Dunn on 3/9/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "AppResultsData.h"
@implementation AppResultsData
@synthesize ANA_NUM,RESULT_ID,ANALYTE_ID,RESULT_AMOUNT,
CSU,MDC,RESULT_UNIT,RESULT_DATE,RESULT_IN_SI,
CSU_IN_SI,MDC_IN_SI,SI_UNIT;
-(id) init{
[super init];
return self;
}
-(void) dealloc
{
[self setANA_NUM:nil];
[self setRESULT_ID:nil];
[self setANALYTE_ID:nil];
[self setRESULT_AMOUNT:nil];
[self setCSU:nil];
[self setMDC:nil];
[self setRESULT_UNIT:nil];
[self setRESULT_DATE:nil];
[self setRESULT_IN_SI:nil];
[self setCSU_IN_SI:nil];
[self setMDC_IN_SI:nil];
[self setSI_UNIT:nil];
}
@end
From the Navigator pane, select "ViewController.h" and add the following:
- UITableViewDataSource and NSXMLParserDelegate to the interface declaration
- soapResults, Page, ItemsPerPage, appResultsData, dataSet, and dataset attributed
- buildURL methods
- Initializations to the {} block.
// ViewController.h
// SampleEPA12
//
// Created by Kevin Dunn on 2/8/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "AppResultsData.h"
@interface ViewController : UIViewController <NSXMLParserDelegate, UITableViewDataSource>
{
NSMutableData *webData;
NSMutableString *soapResults;
NSURLConnection *conn;
//---xml parsing---
NSXMLParser *xmlParser;
NSMutableArray *dataSet;
AppResultsData *appResultsData;
UITableView *resultsTableView;
UIToolbar *ToolBar;
int Page;
int ItemsPerPage;
}
@property (retain, nonatomic) IBOutlet UITableView *resultTableView;
@property (retain, nonatomic) IBOutlet UITextField *AnalyteField;
@property (retain, nonatomic) IBOutlet UIButton *QueryButton;
@property (retain, nonatomic) IBOutlet NSMutableString *soapResults;
@property (retain, nonatomic) IBOutlet UIToolbar *ToolBar;
@property (retain, nonatomic) IBOutlet UIBarButtonItem *Prev;
@property (retain, nonatomic) IBOutlet UIBarButtonItem *Next;
@property ( nonatomic) int Page;
@property ( nonatomic) int ItemsPerPage;
@property (strong, nonatomic) AppResultsData *appResultsData;
@property (nonatomic, retain) NSMutableArray *dataSet;
-(NSString *)buildURL:(NSString *)analyte;
@end
In the ViewController implementation class synthesize the following:
@implementation ViewController
@synthesize AnalyteField;
@synthesize QueryButton;
@synthesize resultTableView;
@synthesize soapResults;
@synthesize appResultsData;
@synthesize dataSet;
@synthesize ToolBar;
@synthesize Prev;
@synthesize Next;
@synthesize Page;
@synthesize ItemsPerPage;
When the ViewController is loaded, the viewDidLoad method is called (this should have been auto generated). Add the following to the viewDidLoad method:
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[QueryButton addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
//UIToolbar *toolBar = [[UIToolbar alloc] init];
ToolBar.hidden=true;
[self setPage:1];
[self setItemsPerPage:10];
}
When the buttons are pressed from the user interface, methods need to be called so they can control what happens when they are pressed. Create the following methods:
NSString *analytId = AnalyteField.text;
[self setPage: Page+1];
Prev.enabled=true;
NSString *url=[self buildURL:analytId];
[self getWebDataFromURL:url];
//[AnalyteField resignFirstResponder];
}
- (IBAction)prevAction:(id)sender {
NSString *analytId = AnalyteField.text;
if(Page<=2){
Prev.enabled=false;
[self setPage: 1];
}else{
[self setPage: Page-1];
}
NSString *url=[self buildURL:analytId];
[self getWebDataFromURL:url];
// [AnalyteField resignFirstResponder];
}
-(void)buttonAction{
NSString *analytId = AnalyteField.text;
NSString *url=[self buildURL:analytId];
[self getWebDataFromURL:url];
[AnalyteField resignFirstResponder];
Prev.enabled=false;
}
In order to make a service call to the EPA, RadNet RESTful Service includes the following method declaration in the ViewController Implementation class. More details on what can be passed to the service can be found here: http://iaspub.epa.gov/enviro/erams_query_v2.simple_query
-(NSString *)buildURL:(NSString *)analyteId{
int startRow = (Page-1)*ItemsPerPage +1;
int endRow = Page*ItemsPerPage;
NSString *urlTemp = @"http://iaspub.epa.gov/enviro/efservice/ERM_RESULT/ANALYTE_ID/";
urlTemp =[urlTemp stringByAppendingString:analyteId];
urlTemp =[urlTemp stringByAppendingString:@"/rows/"];
urlTemp =[urlTemp stringByAppendingString:[NSString stringWithFormat:@"%d", startRow]];
urlTemp =[urlTemp stringByAppendingString:@":"];
urlTemp =[urlTemp stringByAppendingString:[NSString stringWithFormat:@"%d", endRow]];
NSLog(urlTemp);
return urlTemp;
}
-(void) getWebDataFromURL:(NSString *)urlString{
NSString *queryString =[NSString stringWithFormat: urlString];
NSURL *url = [NSURL URLWithString:queryString];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[req addValue:0 forHTTPHeaderField:@"Content-Length"];
[req setHTTPMethod:@"GET"];
// [activityIndicator startAnimating];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
if (conn) {
webData = [[NSMutableData data] retain];
dataSet = [[NSMutableArray alloc] init];
}
}
In order to handle the connection to the Web service the following call back methods need to be implemented.
-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
NSLog(@"DONE. Received Bytes: %d", [webData length]);
if([webData length]>0){
if (xmlParser)
{
[xmlParser release];
}
xmlParser = [[NSXMLParser alloc] initWithData: webData];
[xmlParser setDelegate: self];
[xmlParser setShouldResolveExternalEntities:YES];
[xmlParser parse];
// Tell the UITableView to reload the data
[self.resultTableView reloadData];
//Tool Bar for Next 5
ToolBar.hidden=false;
}
// Connection resources release.
[connection release];
[webData release];
}
-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response {
[webData setLength: 0];
}
-(void) connection:(NSURLConnection *) connection
didReceiveData:(NSData *) data {
[webData appendData:data];
}
-(void) connection:(NSURLConnection *) connection
didFailWithError:(NSError *) error {
[webData release];
[connection release];
}
The NSXMLParser requires some call back methods as well.
-(void) parser:(NSXMLParser *) parser didStartElement:
(NSString *) elementName
namespaceURI:(NSString *) namespaceURI
qualifiedName:(NSString *) qName
attributes:(NSDictionary *) attributeDict
{
if( [elementName isEqualToString:@"ERM_RESULT"])
{
appResultsData = [[AppResultsData alloc] init];
}
//new tag new results
[soapResults setString:@""];
}
-(void)parser:(NSXMLParser *)parser didEndElement:
(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"ERM_RESULT"])
{
[dataSet addObject:appResultsData];
[soapResults setString:@""];
[appResultsData release];
}
if ([elementName isEqualToString:@"ANA_NUM"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setANA_NUM:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"RESULT_ID"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setRESULT_ID:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"ANALYTE_ID"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setANALYTE_ID:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"RESULT_AMOUNT"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setRESULT_AMOUNT:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"CSU"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setCSU:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"MDC"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setMDC:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"RESULT_UNIT"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setRESULT_UNIT:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"RESULT_DATE"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setRESULT_DATE:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"RESULT_IN_SI"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setRESULT_IN_SI:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"CSU_IN_SI"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setCSU_IN_SI:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"MDC_IN_SI"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setMDC_IN_SI:tmpstr];
[soapResults setString:@""];
}
if ([elementName isEqualToString:@"SI_UNIT"])
{
NSString *tmpstr = [[NSString alloc] init];
tmpstr = [tmpstr stringByAppendingString:soapResults];
[appResultsData setCSU_IN_SI:tmpstr];
[soapResults setString:@""];
}
}
-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string
{
if (!soapResults) {
// soapResults is an NSMutableString instance variable
soapResults = [[NSMutableString alloc] init];
}
if([string length] > 0){
NSString *cleanString = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if([cleanString length] >0){
[soapResults appendString: string];
}
}
}
The ViewController also handles the Table View methods. The following methods need to be added to the ViewController implementation class:
#pragma mark - Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//NSLog(@"%s", __FUNCTION__);
NSLog(@"self.dataSet count: %d", [self.dataSet count]);
return [self.dataSet count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"cellForRow called");
static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
SimpleTableIdentifier];
// UITableViewCell cell needs creating for this UITableView row.
if (cell == nil)
{
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
if ([dataSet count] - 1 >= row)
{
// Create a object from the NSMutableArray appData
AppResultsData *appResultsDataDisp = [dataSet objectAtIndex:row];
// Compose a NSString to show UITableViewCell cell
NSString *rowText = [[NSString alloc ] initWithFormat:@"%@ - %@ - %@ - %@ - %@", appResultsDataDisp.RESULT_AMOUNT,appResultsDataDisp.CSU,appResultsDataDisp.MDC_IN_SI,appResultsDataDisp.RESULT_UNIT,
appResultsData.RESULT_DATE];
// Set UITableViewCell cell
cell.textLabel.text = rowText;
cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
// Release alloc vars
[rowText release];
}
return cell;
}
The final step needed to tie everything together is to map the user interface to the methods just created. In the Navigator pane select "MainStoryboard.storyboard". From the interface builder control drag the Table View to the View Controller and select the dataSource from the pop dialog box.
Select the Show the Assistant editor button. Then make sure that "ViewController.m" is selected in the right pane. From the Interface builder to the ViewController.m source code do the following:- Control drag the "Prev" button to the method prevAction (the whole method should be highlighted and then release the button).
- Control drag the "Next" button to the method nextAction (the whole method should be highlighted and then release the button).
Run the application by clicking the "Run" button in xCode. Make sure iPhone 5.0 Simulator is the selected scheme like below.
The iPhone simulator should start and display the following.
Click on the "Analyte ID" text box and type "PU239." Click the "Query" button and it will return a list of results.
Clicking the "Next" button will show the next 10 results.
Locate More Information on Envirofacts Radiation (RadNet) Data
Each dataset has a dataset location path that gives the Uniform Resource Locator (URL) that will be needed to reach the Data. The sample code can be adjusted to get that data and display it as well. For more information on the RESTful Web services provided by the EPA go to Envirofacts Data Service API.


