Integrating WebSphere CloudBurst capabilities in an iPhone solution, Part 3: Creating the controller to manage the view and flow of a Web 2.0 iPhone application

This series of articles walks you through the process of creating a full client application for an Apple® iPhone® device that collaborates with an IBM® WebSphere® CloudBurst™ Appliance using a REST API. Using the vast REST-based APIs provided, WebSphere CloudBurst offers many integration opportunities for a Web 2.0 environment, such as with a smartphone. Building on the previous articles, Part 3 describes how to create a controller class to manage the view and flow of the sample application. This content is part of the IBM WebSphere Developer Technical Journal.

Luca Amato (lucaamato@it.ibm.com), Senior IT Architect, IBM

Author photoLuca Amato is an IBM certified architect who has more than 15 years experience in software development. He has participated as a consultant and project leader in many projects based on web technology. Luca holds a degree in Information Technology from the University of Pisa, Italy, and teaches a language theory course at the University of Pavia for the Informatic department. Luca is an integration solution architect for WebSphere at IBM Italy in Milan, Italy. He works in the WebSphere group with many years of experience in SOA-based solutions. He is also a member of the OpenSource community in IBM.



Alessandro Bartoli (alessandro.bartoli@it.ibm.com), IT Consultant, IBM

Author photoAlessandro Bartoli is an IBM consultant with several years of experience in Software Development. He has worked as a consultant on some IT Governance Management projects and has four certifications in ITIL v3 (Information Tecnology Infrastructure Library). In the past, he also worked as an IT Architect for WebSphere Application Server, WebSphere Portal, and WebSphere Process Server environments. Alessandro holds a Master's degree in Information Technology Engineering from the Politecnico of Milano.



18 May 2011

Also available in Chinese

Prerequisites

Using the controller class to manage the view part of a client application using the iPhone SDK toolkit is the next step in the development of a Web 2.0 application that integrates an IBM WebSphere CloudBurst Appliance with an Apple iPhone device. The application uses the toolkit to implement a Model-View-Controller Framework. In Part 2, you created the view parts and its objects (such as button, text box, and table view). In the controller class, you created the object to manage the collaboration. In this article, you will create the connection between the view controller class and the corresponding view of an iPhone application to manage the flow controller inside the application.

As with the previous installments, this article series assumes a general familiarity with Web2.0 technologies including REST, ATOM, JSON, and Objective-C. The sample application described in these articles is realized using the Model-View-Controller (MVC) pattern in Objective-C using the Apple iPhone SDK 3.x, to be run on an iPhone (or iPod Touch) device with a 3.x firmware. Therefore, this development assumes some basic knowledge of the C programming language (Object-C inherits all of C's features). It is also important that you understand basic object-oriented paradigms, such as inheritance, class, interface, and so on.

Also, this exercise uses the Cocoa framework, which consists of a set of APIs, libraries, and run time code that forms the development layer for the iPhone SDK. Cocoa is implemented in Object-C and uses the MVC pattern to encapsulate views, application data, controllers and the class to mediate the management logic.

Finally, to perform the steps and run the examples from this article, you will need a computer with the Apple SDK 3.0 or higher installed, running a compatible version of Mac OS. You also need access to a WebSphere CloudBurst Appliance running in your environment


Complete OneWindow view

Every time you start to create a Cocoa project, you need to choose a template. For this exercise, you will choose the Tab Bar Application template. This template generates two windows (see Figure 1) and a tab bar that lets you switch between them.

  • First View: MainWindow.xib
  • Second View: SecondView.xib
Figure 1. Application view
Application view

If you open the View Component windows of MainWindows.xib, you will see the structure shown in Figure 2. It is composed of:

  • File's Owner (delegate to the Test App Delegate)
  • First Responder
  • Test App Delegate
  • Windows (generic window not used)
  • Tab Bar Controller (The tab bar controller that manages two View Controllers: the First View and the Second View.)
    • Tab Bar
    • First View controller (for the First View)
    • View (view managed by FirstViewController)
    • Tab Bar Item (items in the Bar Controller for First View)
    • Second View controller (for the Second View)
    • View (view managed by FirstViewController)
    • Tab Bar Item (items in the Bar Controller for the Second View)
Figure 2. MainWindow structure
MainWindow structure

Insert the Navigation Controller

The Navigation Controller manages the navigation among views in an application window's flow. As discussed in Part 2, you will design the application with a simple navigation among three screens. To do this, you need to define a Navigation Controller in the MainWindows.

The Navigation Controller has the following parts:

  • Navigation Controller: This is the controller object that handles navigation events and wires it all up together.
  • Navigation Bar: This is the bar along the top that enables the user to see where they are going through a navigation hierarchy, and enables them to navigate backwards.
  • View Controller: This is the View Controller class related to the hold view.
  • Navigation Item: The navigation item is in the Navigation Bar tile and has the button for navigating.

In the MainWindow.xib component window, change the first view controller defined in the Tab Bar Controller component.

  1. Select Tab bar controller in the MainWindow component window.
  2. Select Attribute View and change the class of the View Controller named "First" from View Controller to Navigation Controller, as shown in Figure 3.
    Figure 3. Navigation Controller class definition
    Navigation Controller class definition

Through the Navigation Controller class, it is possible to manage the navigation among the application views. In this project, you will create the first view in the OneView.xib file (with its OneViewController class). You need to substitute the MainWindow with OneView.

  1. Select the windows that contains your First view by clicking the top window.
  2. Select the First View panel inside the view and delete them. Note: Do not select the label. In Figure 4, you can see the view after the deletion.
    Figure 4. Main View
    Main View
  3. In the view window, be sure to select the empty view by clicking inside the gray part of the View.
  4. In the View Controller attributes, insert the NIB file using the drop down list: OneView.
    Figure 5. OneView
    OneView
  5. Click on the One View blue link in the middle of the panel on the left to open the One View view.
  6. Select File's Owner in the View Component view.
  7. Assign the OneViewController view controller class for the selected One View view using the Class drop down list. This means the OneViewController class is the controller of the OneView view (Figure 6).
    Figure 6. OneView controller class
    OneView controller class
  8. Save all your changes.
  9. You can test your application by selecting Build > Build and Run.

You can now see now your OneView as the first open window in the application.

Connect the view components

When you created the OneViewController class, you defined instance variables to manage the view components. These variables are:

  • field1: For the first entry field
  • field2: For the second entry field
  • description: For a test text field

You need to assign these variables to each view component. Let's look at how to connect field1. (You will repeat this action for all the view components with the corresponding instance variables.)

  1. Open OneView in the Interface Builder.
  2. Select File's Owner. It should be the OneViewController class.
  3. Using the right mouse button, drag the File's Owner row over the first text field as shown in Figure 7.
    Figure 7. Assign instance variable
    Assign instance variables
  4. Select from the menu that appears at the end of the drag action the variable assigned to this component.
    Figure 8. Variable table
    Variable table
  5. Repeat the same action for all the view components. Select for each view component the corresponding instance variable.

At the end, all your instance variables are now assigned to the view components.

Assign a button action

In OneView, you have the following three buttons:

  • test : To be assigned to (IBAction) buttonPressedTest
  • load : To be assigned to (IBAction) buttonDownload
  • next : To be assigned to (IBAction) buttonNext

You need to assign each action to the relative button view component. You will now connect the test button. (You will repeat the same action for all buttons with the corresponding method.)

  1. Select the Test button in the OneView component window.
  2. In Button Connection, you can see all the button events.
  3. Using the left mouse button, drag the action that you want to manage, such as Touch Down, over to the File's Owner row.
  4. Select the method from the list that appears at the end of the drag action (for example, buttonPressedtest).
  5. Repeat the same action for all the buttons. Select for each button component the corresponding method.
    Figure 9. OneViewController connections
    OneViewController connections

Complete the OneViewController class

Now you can insert in the class all the required code to manage buttonPressedTest and buttonNext. The buttonPressedTest method loads the test file, example.sql, in the responseString variable (see Listing 1).

Listing 1. (IBAction) buttonPressedTest
<![CDATA[
(IBAction) buttonPressedTest
{
[field1 resignFirstResponder];
[field2 resignFirstResponder];
NSString *filePath = [[NSBundle mainBundle] 
	pathForResource:@"example" 
	ofType:@"sql"];
NSString *responseString = [NSString stringWithContentsOfFile:filePath];
NSLog(@"responseString:%@",responseString);

description.text = responseString;
}]]>

The example.sql file contains the items shown in Listing 2.

Listing 2. example.sql
[
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Stopped",
      "created": 1260941860895,
      "name": "Single Server",
      "currentstatus": "RM01011",
      "desiredstatus": null,
      "currentmessage": "RM07153",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/6",
      "updated": 1260943821497,
      "id": 51,
      "currentmessage_text": "Stopped"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Running",
      "created": 1263868560454,
      "name": "05WASadmin Cloned Lab Virtual System",
      "currentstatus": "RM01005",
      "desiredstatus": "",
      "currentmessage": "RM07054(\"05WASAdmin+Lab+Application\",\"05WASadmin+Cloned+Lab+
      Virtual+System+vm-001-016+dmgr\")",
      "pattern": "/resources/patterns/43",
      "owner": "/resources/users/15",
      "updated": 1263869039755,
      "id": 59,
      "currentmessage_text": "script package of 05WASAdmin Lab Application
      on VM 05WASadmin Cloned Lab Virtual System vm-001-016 dmgr"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Stopped",
      "created": 1263857152896,
      "name": "Financial Application Test Group Sys 05",
      "currentstatus": "RM01011",
      "desiredstatus": null,
      "currentmessage": "RM07153",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/15",
      "updated": 1263863898994,
      "id": 54,
      "currentmessage_text": "Stopped"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Run",
      "created": 1263857159858,
      "name": "John",
      "currentstatus": "RM01006",
      "desiredstatus": "",
      "currentmessage": "RM07045",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/14",
      "updated": 1263857536925,
      "id": 55,
      "currentmessage_text": "The VM is available"
   },
   {
      "desiredstatus_text": null,
      "currentstatus_text": "Run",
      "created": 1263862881511,
      "name": "LucaV",
      "currentstatus": "RM01006",
      "desiredstatus": "",
      "currentmessage": "RM07045",
      "pattern": "/resources/patterns/1",
      "owner": "/resources/users/4",
      "updated": 1263863256864,
      "id": 56,
      "currentmessage_text": "The VM is available"
   }
]

The buttonNext method will call the ListElement view and instantiate ListElementViewController, as shown in Listing 3.

Listing 3. (IBAction) buttonNext
<![CDATA[
-(IBAction) buttonNext
{
[field1 resignFirstResponder];
NSString *server = field1.text;
ListElementViewController *listElementController = 
	[[ListElementViewController alloc] 
		initWithNibName:@"ListElementView" 
		bundle:nil];
NSLog(@"func UnoViewController.buttonNext ");
[listElementController setDescpText:server];
[self.navigationController pushViewController:listElementController 
 animated:YES];
]]>
[listElementController loadJsonText:description.text];
<![CDATA[	
}
]]>

The loadJsonText method injects the data received from WCA to ListElementViewController. The received data is used to display the list on the view.


Complete the ListElement view

The ListElement view is composed by only one view component, a Table View component. This component does not have an assigned instance variable inside the view controller class, but you need to define the class delegate. This is necessary because you need to define many methods to manage all the necessary table view actions. The framework will call the class delegate every time it is necessary, and the class must implement all required methods. These methods are:

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  • (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  • (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  • (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

Create these methods inside the class delegate. Define the class delegate of the ListElement view. To simplify, you will use the same class (such as ListElementViewController) for two methods: view controller and table view delegate.

  1. Open the ListElementView.xib file and insert the ListElementViewController class in the Identity view.
  2. Select the Table View component in the view component window.
  3. Select the Table View connection.
  4. Using the left mouse button, drag from the dataSource tag to File's Owner in the view component window.
  5. Do the same for the delegate tag.

Figure 10 shows the Table View connections at the end.

Figure 10. Table view dataSource and delegate
Table view dataSource and delegate

Managing the JSON file

This view receives the JSON data file from the caller view controller. In this solution, the previous view manages the network connection protocol. This view, will manage the data instead (it does not know where the data arrived), but it must convert the text string (JSON-like formatted) into an Objective-C data object. To grab the dictionary, you will use the VirtualSystems class. As discussed in Part 2, you will include in this class the JSON parser. To do this at the top of the class declaration, you need to include:

  • #import "JSON/JSON.h": This should be different according to your JSON parser choice.
  • #import "VirtualSystems.h": This includes the class to grab the JSON data object. Each element found in the JSON text file is converted into a VirtualSystems object.

The connection between the caller class (OneViewController) and ListElementViewController is performed with the following code method as shown in Listiing 4.

Listing 4. IBAction) loadJsonText:(NSString *)jsonString.h
<![CDATA[
- (IBAction) loadJsonText:(NSString *)jsonString{
NSLog(@"func UnoViewController.loadJsonText :%@ ",jsonString);
	
NSError *error;]]><![CDATA[
SBJSON *json = [[SBJSON new] autorelease];
NSArray *luckyNumbers = [json objectWithString:jsonString 
 error:&error];
]]>
	
<![CDATA[for (int i = 0; i < [luckyNumbers count]; i++) 
{
	NSLog(@"number:%@",[luckyNumbers objectAtIndex:i]);
	NSDictionary *tmpDict= [luckyNumbers objectAtIndex:i];
		
	VirtualSystems *elem = [[VirtualSystems alloc] 
						initWithJSON:tmpDict name:@"test"];
	[listElement addObject:elem];
	[elem release];
}
}
]]>

You can see the code in bold, depending on your choice of the JSON parser. Change these lines of code according to the parser instruction. In this example, this method uses an SBJSON object to parse and obtain a data object array. For each element found in this array, you will create a VirtualSystems object and load it in an instance variable, listElement.

As you can see, we used an NSDictionary object. A dictionary in Objective-C is an object that holds two sets of information, a unique key and associated values, and enables you to use the key to retrieve items. You should use this kind of object when you need a convenient and efficient way to retrieve data associated with an arbitrary key. The key is usually an Integer or a string, and the item is of any type. Since you used NSDictionary, you created a static dictionary. Each row composed of the key and related values is called an entry. Within a dictionary, keys are unique. Neither a key nor a value can be null (if a null value in a dictionary is required, use "NSNull" instead). You extracted data from the JSON file and mapped information in the Dictionary object file called tmpDict. According to the used JSON structure, the Dictionary object has the following fields:

  • desiredstatus_text
  • currentstatus_text
  • created
  • name
  • currentstatus
  • desiredstatus
  • currentmessage
  • pattern
  • owner
  • updated
  • id
  • currentmessage_text

These fields will contain values extracted from the JSON file for each item. There will be one entry for each node of the JSON structure. In this case, you will have five entries.


Creating the Table row

In the ListElement view, you will insert a simple Table view. You will create a complex list with more labels and an icon inside the single row table. There are many ways to do this. The method being used here does not use any graphical interface, so you will insert each view component inside the table view using code.

This feature is implemented in the cellForRowAtIndexPath method. This method is called every time the system needs to understand how to build any single row of the table view. This way, you can create rows different from each other. To reduce the method effort, you will build the method in two different blocks:

  • cell==nil: This test is true only for the first time. You will create the cell for the row.
  • cell!=nil: This means that the cell row is already created. You need to assign only the value to the cell tag. In this case, skip the view cell table creation.

Using this method, you need to set the coding (during the cell creation) using the correct component location in the screen layout. To do this, you need to define the correct Frame rectangle point by point (Listing 5).

Listing 5. cellForRowAtIndexPath method
<![CDATA[
- (UITableViewCell *)tableView:(UITableView *)tableView 
   cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"ImageOnRightCell";
UILabel *mainLabel, *secondLabel;
UIImageView *photo;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
 CellIdentifier];

if (cell == nil) {
	cell = [[[UITableViewCell alloc]
		initWithStyle:UITableViewCellStyleDefault 
		reuseIdentifier:CellIdentifier] autorelease];
	cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
		
	mainLabel = [[[UILabel alloc]
		initWithFrame:CGRectMake(0.0, 0.0, 290.0, 18.0)] autorelease];
	mainLabel.tag = MAINLABEL_TAG;
	mainLabel.font = [UIFont systemFontOfSize:14.0];
	mainLabel.textAlignment = UITextAlignmentRight;	
	mainLabel.textColor = [UIColor blackColor];
	mainLabel.autoresizingMask = 
	 UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
	[cell.contentView addSubview:mainLabel];

	secondLabel = [[[UILabel alloc]
	 initWithFrame:CGRectMake(0.0, 20.0, 265.0, 25.0)] autorelease];
	secondLabel.tag = SECONDLABEL_TAG;
	secondLabel.font = [UIFont systemFontOfSize:12.0];
	secondLabel.textAlignment = UITextAlignmentRight;
	secondLabel.textColor = [UIColor darkGrayColor];
	secondLabel.autoresizingMask = 
	 UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
	[cell.contentView addSubview:secondLabel];

	photo = [[[UIImageView alloc]
	 initWithFrame:CGRectMake(270.0, 18.0, 25.0, 25.0)] autorelease];
	photo.tag = PHOTO_TAG;
	photo.autoresizingMask = 
	 UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
	[cell.contentView addSubview:photo];
} else {
	
	mainLabel = (UILabel *)[cell.contentView viewWithTag:MAINLABEL_TAG];
	secondLabel = (UILabel *)[cell.contentView viewWithTag:SECONDLABEL_TAG];
	photo = (UIImageView *)[cell.contentView viewWithTag:PHOTO_TAG];
}

VirtualSystems *vs = (VirtualSystems *) [listElement  
 objectAtIndex:indexPath.row];
mainLabel.text = [vs getName];
secondLabel.text =  [NSString stringWithFormat:@"%i",[vs getId]];
NSString *status=[vs getCurrentstatus];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:status 
 ofType:@"png"];
UIImage *theImage = [UIImage imageWithContentsOfFile:imagePath];
photo.image = theImage;
return cell;
}
]]>

Opening the DetailSystem view

When the user selects a specific row in the table, you need to identify the virtual system shown in the selected row, retrieve its data from the listElement array, and call ListElementView and its view controller.

The method didSelectRowAtIndexPath will be called by the view when the select action is performed. The method receives the index of the selected element in the list, and you will use this index to identify the listElement element, extract it, and push it in DetailSystemViewController using the setVirtualSystem behavior, as shown in Listing 6. Before leaving the view, deselect the selected row.

Listing 6. didSelectRowAtIndexPath method
<![CDATA[
- (void)tableView:(UITableView *)tableView 
	didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	
VirtualSystems *elem = (VirtualSystems *)[listElement 
 objectAtIndex:indexPath.row];
DetailSystemViewController *dettaglioController = 
	[[DetailSystemViewController alloc] 
			initWithNibName:@"DetailSystemView" bundle:nil];
NSLog(@"func didSelectRowAtIndexPath elem.keyID:%i",indexPath.row);
[dettaglioController setVirtulaSystem: elem];
[self.navigationController pushViewController:dettaglioController 
 animated:YES];
	
[tableView deselectRowAtIndexPath:indexPath animated:YES];	
}
]]>

Completing the DetailSystem view

This view has received a single system's information as an instance of the VirtualSystems object. This object is the element selected by the user in the ListElement view. The object is stored in the aSystem instance variable using the behavior of -(void)setVirtualSystems:. The values inside the object are used to assign the information to the view element and to display it to the user. You will use icons to describe the system status (you can find icon examples in the Downloads sectione, but you can use other icons as well):

  • RM01011: System stopped
  • RM01006: System running
  • RM01005: System status not knows

Load the selected icons into the project:

  1. Load your icons inside the Mac file system.
  2. Rename them to RM01005.png, RM01006.png, and RM01011.png, respectively.
  3. Choose Add > Existing Files as described in the first article of this series.

To simplify the code, assign the icons the same status code retrieved by the REST syndication.

You will set the assign values, from the aSystem instances variable, to the view components in the -(void)viewDidLoad method. In Listings 7 and 8, you can see the DetailSystemsViewController code.

Listing 7. DetailSystemsViewController.h
<![CDATA[
//
//  DetailSystemViewController.h
//  WCA000
//
//  Created by luca amato on 9/27/10.
//

#import <UIKit/UIKit.h>
#import "VirtualSystems.h"

@interface DetailSystemViewController : UIViewController {
IBOutlet UILabel *name;
IBOutlet UILabel *currentStatusText;
IBOutlet UILabel *created;
IBOutlet UILabel *owner;
	
IBOutlet UIImageView *statusImg;

IBOutlet UITextView *currentMessageText;
	
VirtualSystems *aSystem;
}

@property (nonatomic, retain) VirtualSystems *aSystem;

- (void)setVirtulaSystem:(VirtualSystems *)system;

@end]]>
Listing 8. DetailSystemsViewController.m
<![CDATA[
//
//  DetailSystemViewController.m
//  WCA000
//
//  Created by luca amato on 9/27/10.
//

#import "DetailSystemViewController.h"

@implementation DetailSystemViewController

// Implement viewDidLoad to do additional setup 
// after loading the view, typically from a nib.
- (void)viewDidLoad {
NSString *status=[aSystem getCurrentstatus];
NSString *imagePath = [[NSBundle mainBundle] 
 pathForResource:status ofType:@"png"];
statusImg.image = [UIImage imageWithContentsOfFile:imagePath];
	
self.title=[NSString stringWithFormat:@"%i",[aSystem getId]];
name.text=[aSystem getName];
created.text=[aSystem getCreated];
currentStatusText.text = [aSystem getCurrentstatusText];
currentMessageText.text = [aSystem getCurrentmessage];
NSObject *tmp=[aSystem getCreated];
	
[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}

- (void)dealloc {
    [super dealloc];
}

- (void)setVirtulaSystem:(VirtualSystems *)system {
	aSystem = system;
}

@end]]>

Now you can save the file. Choose File > Save from the option menu.


Saving the application status

In many user applications, it is useful to save the application status. The saved information will be retrieved the next time you start the application, and in the correct fields, the last values used in the application.

There are many ways to do this. Here, you will implement this feature using a simple file. The SDK framework provides a way to encode objects (including scalar values) into an architecture-independent format. You can use this class to store the objects in a file. This keyed archive is realized with NSKeyedArchiver. Refer to the Cocoa library reference documentation for more details. You will retrieve the data when the first application class (OneViewController) is instantiated. The first time, you must check if the archived file is present. If it is not present, you must create an empty file. When you dispose the last class, you will write the status information into the file.

The status class

For a good object-oriented approach, you will create a specific class to manage all the necessary status key values. You are supposed to have four values to store in the status file. Therefore, create the FourLines class. This class manages four values, each value has a specific field key. You will define the field key value as constant in the h file: Field1,Field2,Field3,Field4.

Save the server name (from field1) in the Field1 FourLine key, the User ID (from field2) in the Field2 FourLines key (Listings 9 and 10). For security reasons, the password is not stored in the application status.

Listing 9. FourLines.h
<![CDATA[
//
//  FourLines.h
//  WCA000
//
//  Created by luca amato on 9/27/10.
//

#import <Foundation/Foundation.h>
#define FIELD1KEY @"Field1"
#define FIELD2KEY @"Field2"
#define FIELD3KEY @"Field3"
#define FIELD4KEY @"Field4"

@interface FourLines : NSObject <NSCoding, NSCopying> {
	NSString *field1 ;
	NSString *field2 ;
	NSString *field3 ;
	NSString *field4 ;
}

@property (nonatomic, retain) NSString *field1;
@property (nonatomic, retain) NSString *field2;
@property (nonatomic, retain) NSString *field3;
@property (nonatomic, retain) NSString *field4;
@end
]]>
Listing 10. FourLines.m
<![CDATA[
//
//  FourLines.m
//  Persistence
//
//  Created by luca amato on 9/11/10.
//

#import "FourLines.h"


@implementation FourLines

@synthesize field1;
@synthesize field2;
@synthesize field3;

@synthesize field4;

#pragma mark NSCoding
-(void)encodeWithCoder:(NSCoder *)encoder
{
	[encoder encodeObject:field1 forKey:FIELD1KEY];
	[encoder encodeObject:field2 forKey:FIELD2KEY];
	[encoder encodeObject:field3 forKey:FIELD3KEY];
	[encoder encodeObject:field4 forKey:FIELD4KEY];
}

-(id)initWithCoder:(NSCoder *)decoder
{
	if (self = [super init])
	{
		self.field1 = [decoder decodeObjectForKey:FIELD1KEY];
		self.field2 = [decoder decodeObjectForKey:FIELD2KEY];
		self.field3 = [decoder decodeObjectForKey:FIELD3KEY];
		self.field4 = [decoder decodeObjectForKey:FIELD4KEY];
	}
	return self;	
}

#pragma mark -
#pragma mark NSCopying
-(id)copyWithZone:(NSZone *)zone
{
	FourLines *copy = [[[self class] allocWithZone:zone] init];
	field1 = [self.field1 copy];
	field2 = [self.field2 copy];
	field3 = [self.field3 copy];
	field4 = [self.field4 copy];
	
	return copy;
}
@end
]]>

Saving and retrieving the data status

Part of the code shown in Listing 11 retrieves the saved file and extracts the specific field values from the FourLines class.

Listing 11. Retrieve FourLines
<![CDATA[	.
	.
NSData *data = [[NSMutableData alloc] 
 	initWithContentsOfFile:[self dataFilePathPersistence]];
NSKeyedUnarchiver *unarch = [[NSKeyedUnarchiver alloc] 
 	initForReadingWithData:data];
FourLines *fourLines = [unarch decodeObjectForKey:DATAKEY];
[unarch finishDecoding];
field1.text = fourLines.field1;
field2.text = fourLines.field2;

[unarch release];
[data release];
	.
	.]]>

Part of the code in Listing 12 saves the FourLines class.

Listing 12. Save FourLines
<![CDATA[	.
	.
FourLines *fourLines = [[FourLines alloc] init];
fourLines.field1 = field1.text;
fourLines.field2 = field2.text;
fourLines.field3 = @"uno";
fourLines.field4 = @"due";
	
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] 
 initForWritingWithMutableData:data];
[arch encodeObject:fourLines forKey:DATAKEY];
[arch finishEncoding];
[data writeToFile:[self dataFilePathPersistence] atomically:YES];
	.
	.]]>

You will insert these parts of code in the OneViewController class and the retrieve method code in the -(void)DidLoad method. This will be called after the OneView is loaded.

The saved code in the -(void)applicationWillTerminate method is called when the OneView is destroyed. In Listing 13, you can see the complete code of the OneViewController class that works in unplugged mode. As you can see, the JSON data is retrieved from a static file called example.sql.

Listing 13. OneViewController.m
<![CDATA[
 //
//  UnoViewController.m
//  WCA000
//
//  Created by luca amato on 9/25/10.
//

#import "UnoViewController.h"
#import "ListElementViewController.h"

#import "FourLines.h"
#import "AlertPrompt.h"

@implementation UnoViewController

@synthesize inputString;
@synthesize field1 ;
@synthesize field2 ;
@synthesize description;

 // Implement viewDidLoad to do additional setup 
 // after loading the view, typically from a nib.
 - (void)viewDidLoad {
self.title = @"WCA Server";
	 
NSString *filePath = [self dataFilePathPersistence];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSData *data = [[NSMutableData alloc] 
initWithContentsOfFile:[self dataFilePathPersistence]];
NSKeyedUnarchiver *unarch = [[NSKeyedUnarchiver alloc] 
initForReadingWithData:data];
FourLines *fourLines = [unarch decodeObjectForKey:DATAKEY];
[unarch finishDecoding];
field1.text = fourLines.field1;
field2.text = fourLines.field2;

[unarch release];
[data release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] 
	addObserver:self 
	selector:@selector(applicationWillTerminate:) 
	name:UIApplicationWillTerminateNotification object:app
	];
	
[super viewDidLoad];
}
 
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
}

- (void)dealloc {
[super dealloc];
}

-(NSString *)dataFilePath
{
NSArray *path = NSSearchPathForDirectoriesInDomains
	(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent: FILENAMEARCH];
}

-(IBAction) buttonDownloadImage
{
}	

-(IBAction) buttonPressedTest
{
[field1 resignFirstResponder];
[field2 resignFirstResponder];
NSString *filePath = [[NSBundle mainBundle] 
	pathForResource:@"example" ofType:@"sql"];
NSString *responseString = [NSString stringWithContentsOfFile:filePath];
NSLog(@"responseString:%@",responseString);
description.text = responseString;
}

-(IBAction) buttonNext
{
[field1 resignFirstResponder];
NSString *server = field1.text;
ListElementViewController *listElementController = 
	[[ListElementViewController alloc] 
			initWithNibName:@"ListElementView" bundle:nil];
NSLog(@"func UnoViewController.buttonNext ");
[listElementController setDescpText:server];
[self.navigationController pushViewController:listElementController 
 animated:YES];
[listElementController loadJsonText:description.text];
}

-(NSString *)dataFilePathPersistence
{
NSArray *path = NSSearchPathForDirectoriesInDomains
	(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [path objectAtIndex:0];
return [documentDirectory stringByAppendingPathComponent: FILENAMEARCH];
}

-(void)applicationWillTerminate:(NSNotification *)notification
{
FourLines *fourLines = [[FourLines alloc] init];
fourLines.field1 = field1.text;
fourLines.field2 = field2.text;
fourLines.field3 = @"uno";
fourLines.field4 = @"due";
	
NSMutableData *data = [[NSMutableData alloc] init];
NSKeyedArchiver *arch = [[NSKeyedArchiver alloc] 
	initForWritingWithMutableData:data];
[arch encodeObject:fourLines forKey:DATAKEY];
[arch finishEncoding];
[data writeToFile:[self dataFilePathPersistence] atomically:YES];
[fourLines release];
[arch release];
[data release];
}
@end
]]>

Save and test

Be sure to save all the files. Now you can test the application by selecting Build > Build from the Go menu. If you find some errors, remove them and recompile the project.

To test the application in the main screen, click the Test button. At the bottom, you can see the test file. Click the Next button. The window displays a list of servers that you defined in the example JSON file. You can select a system that is present in a row. The application will then display the selected system details in the next view (DetailSystemView). You can go back, in each view, using the button in the navigation tool bar that is on the top of the view. With the back button, you see the title of the previous view.


Conclusion

In this article, you created the controller part of the MVC framework using the Cocoa framework. The controller class manages the GUI, implements the Navigation Controller, connects the view components, and assigns actions to each button of the GUI. However, the application does not manage the HTTPS protocol. To communicate with a WebSphere Cloudburst Appliance, you will need to implement the network element, including security management, which is described in the conclusion to this series.


Download

DescriptionNameSize
Sample code1105_amato1_WCAsample.zip1.5MB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Web development
ArticleID=656638
ArticleTitle=Integrating WebSphere CloudBurst capabilities in an iPhone solution, Part 3: Creating the controller to manage the view and flow of a Web 2.0 iPhone application
publish-date=05182011