Developing a social networking iPhone application based on IBM Lotus Connections 2.5

IBM® developerWorks® recently introduced its first native iPhone application to the Apple Application Store, a great tool for users to explore and discover developerWorks content and community, connect with other IT professionals and subject matter experts, and seek out opportunities to share experience.

Share:

Nicholas Poore (npoore@us.ibm.com), Advisory Software Engineer, IBM China

An advisory software engineer for IBM developerWorks, Nick Poore is the chief community architect for My developerWorks and has worked on assignment for the Lotus Connections 3.0 product delivery team. Nick combined his enthusiasm for the Apple iPhone with his knowledge of Lotus Connections to develop one of IBM’s first native iPhone applications. Learn more in Nick’s profile on developerWorks.



Ami Dewar, developerWorks Advanced design team lead, IBM

Ami Dewar is the developerWorks Advanced design team lead. Advanced design is a hybrid team of designers, developers, and architects working to keep developerWorks on the cutting edge. Ami, formerly the lead graphic designer, has been a designer with IBM for six years, creating over 1200 feature graphics and 8000 technical illustrations. Learn more in Ami's profile on developerWorks



12 October 2010 (First published 08 October 2010)

Also available in Russian Portuguese

Introduction

developerWorks recognized that mobile devices are now a preferred mode of communication and content syndication,  and the Apple iPhone specifically has garnered significant popularity around the world.   Additionally, IBM Lotus® Connections 2.5, the products underlying the My developerWorks community, now offers the optimal user experience that emulates some of the most popular mobile social networking applications.  

The developerWorks iPhone application highlights many of the most popular social networking features of Lotus Connections:

  • A view of public updates, allowing users to see all the activity happening in the community, search by tag, keyword, or user name to find My developerWorks content and users
  • The ability to keep up with “My updates,” post status updates to profiles, or add a comment to a colleague’s profile

All these features were accomplished by using the Lotus Connections 2.5 public APIs.

As an introduction to using Lotus Connections 2.5 APIs, we take you through the steps of creating a simple iPhone application that displays the most recent blog entries from My developerWorks. This process required us to understand how to access the blogs API, download the data from the feed, parse the data, and display the data correctly on the iPhone.


Navigating the Lotus Connections 2.5 APIs  

Lotus Connections offers a great set of public application programming interfaces (APIs) that have many development uses. The first step in understanding how to use the APIs for any of your development needs, whether for an iPhone application or another use, is to understand how to navigate the API documentation.

The Lotus Connections applications (Activities, Blogs, Bookmarks, Communities, Files, Profiles, and Wikis) and the Lotus Connections home page all provide APIs that enable you to integrate them with other applications such as the iPhone application. Using the APIs, you can programmatically access and utilize the same data that you see displayed in the Lotus Connections community suite, such as all the community activity on My developerWorks. The Lotus Connections APIs are based on Atom Syndication Format, which allows two-way communication (read/write). To use the APIs, your programming language only has to be able to send and receive XML over HTTP.

The Lotus Connections APIs are documented in the Lotus Connections Information Center. For the iPhone application, our focus was on attaining the ongoing user updates to our community so we found documentation titled “Getting a feed of all our public updates.” This documentation pointed us to the Updates APIs. The APIs for Updates allowed us to get the most recent (public) activity generated by users on My developerWorks. We reference the same Updates APIs for the Blog example. To find these APIs go to Developing - Lotus Connections APIs - Updates API - Getting updates feeds - Getting a feed of all public updates.

Now it’s important to understand how to use the feed to attain the feed you need for your application.

Table 1 describes the resource to get the content from the API. A resource is a URL within the API that allows you to access the data. This resource gives us the root URL to access all public updates within Lotus Connections.

Table 1. Atom API request details
ResourceDescription
/atom/stories/publicGets a feed of all public updates

This URL retrieves all the public updates; in this example, we are interested only in a single application, Blogs, as the feed we need returned.

Table 2 describes the parameters, such as identifying an application, Blogs, that can be used to allow the application to retrieve the filtered content wanted.

Table 2. Parameters to filter the Lotus Connections feed
ParameterDescription
beforeReturns only those items that were last modified before the date specified by this parameter. Format the date using the syntax: 2008-02-07T21:07:56Z.
containerUnique ID of one of the following items:

  • An activity in the Activities application
  • A blog in the Blogs application
  • A person's bookmark collection in the Bookmarks application
  • A community in the Communities application
  • A file in the Files application
  • A person's profile in the Profiles application
  • A wiki in the Wikis application

Use this parameter with the source parameter. In the source parameter, specify the application in which this unique ID for the item is valid. This parameter is ignored if a valid application name is not specified in the source parameter.

emailEmail address of a person. Use this parameter to limit the feed of updates to contain only updates generated by a specific person. Do not use this parameter if Lotus Connections is configured to hide email addresses; use user ID instead.
lang Use this parameter to request stories in a different language than specified in the HTTP request. To specify the language, use one of the following formats:

  • Java™ format (for example, en_IE, es_GT)
  • Dojo format (for example, en-ie, es-gt)
pagePage number. Specifies the page to be returned. The default is 1.
psPage size. Specifies the number of entries to return per page. The maximum number allowed is 100.
sinceReturns only those items that were last modified after the date specified by this parameter. Format the date using the syntax: 2008-02-07T21:07:56Z.
sourceSpecify a single feature from which you want the feed of updates to be returned. Options are:

  • Activities
  • Blogs
  • Bookmarks
  • Communities
  • Files
  • Profiles
  • Wikis
useridUnique ID of a person. Use this parameter to limit the feed of updates to contain only updates generated by a specific person. The value of this parameter takes precedence when both this parameter and the email parameter are specified.

We are also interested in the 30 most recent blog updates as opposed to the default setting of 20 entries. We make this designation using the page size parameter (ps).

The feed to view the 30 most recent blog updates would look like this:

https://www.ibm.com/developerworks/mydeveloperworks/news/atom/stories/public?source=blogs&ps=30

Some of the other commonly used parameters are language, since, and userid. It is important to note that the language type return parameter doesn’t translate the user-contributed content; however, it does translate the standard labels or UI associated with this content. The parameters before and since allow you to apply time stamps and limit the data retrieved to only those updates that occurred before or after a specific date. Userid allows you to limit your updates to those generated by a specific user.

Even though we are demonstrating only a single feed for the sake of the sample application, the My developerWorks iPhone application takes advantage of the Lotus Connections APIs to display all user-contributed content, filtered by application, search results, profile data, and more.


Attaining data for the application and parsing it using the Lotus Connections APIs

Now that we understand how to access the Lotus Connections data using the APIs, the next step is to start writing the iPhone application. We use the Apple xCode template generator to create a View-based Application iPhone project called SampleApplication (see figure 1).

Figure 1. New xCode Project window
New xCode Project window

When the project is initiated, several files are automatically created, including the SampleApplicationViewController.h and SampleApplicationViewController.m files. Viewing the SampleapplicationViewController.m file, you can see the viewDidLoad method. This method is called when the view is loaded in the application. Adding the connection request to the Lotus Connections APIs feed in the viewDidLoad method initiates the request to the API when the view is loaded and the user launches the application. To start using the API, we first have to define an NSURLRequest, which is where you add the URL of the ATOM feed that you attained in the first section.

After we have an NSURLRequest object, we create an NSURLConnection and pass it the NSURLRequest and give it the delegate. An NSURLConnection is a simple interface used for communication between your iPhone application and Lotus Connections through the Atom APIs. Creating a connection using NSURLConnection is straightforward. It requires the application to provide a delegate object to which that delegate implements the following methods:

  • connection:didReceiveResponse:
  • connection:didReceiveData:
  • connection:didFailWithError:
  • connectionDidFinishLoading:

We give it a delegate because the NSURLConnection loads the request asynchronously and needs to know what to do with the NSData returned from the NSURLConnection.

Before we go any further, let’s define a delegate object according to the Apple developer guide. A delegate object or delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object, the delegate, and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. In this case, when the data is returned from the NSURLConnection it calls the delegate object and informs it that the data is ready to be parsed so that it can be displayed to the user.

Listing 1 shows the code used to initialize a connection with NSURLRequest in the sample application. If the connection is successful, it sets the networkActivityIndicatorVisible to YES. This setting displays a visual indicator on the iPhone UI that there is network activity in progress.

Listing 1.Create a NSURLRequest
//Blogs Atom feed API
  NSURL *url = [[[NSURL alloc]
    initWithString:@"https://www.ibm.com/developerworks/mydeveloperworks/news/
atom/stories/public?source=blogs&ps=30"]
	 autorelease];

  NSURLRequest* request = [[NSURLRequest alloc]
	   initWithURL:url];



  NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request 
delegate:self];		
  if (connection) {
	//conection is good turn on the network indicator spinner
	[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
  } else {
	 //error	
}

The next step is to define the method that is called when the connection responds. In this method, we get the Content-Length from the header in the response object. We use this value to define the size of the NSMutableData object, which is where we store all the data returned from the request. See listing 2.

Listing 2. Process the NSURLConnection response
////////////////////////////////////////////////////////////////////////////////////
- (void)connection:(NSURLConnection*)connection didReceiveResponse:
(NSHTTPURLResponse*)response {
	//response saved so that status Codes can be checked later
    _response = [response retain];
    NSDictionary* headers = [response allHeaderFields];
    int contentLength = [[headers objectForKey:@"Content-Length"] intValue];
    
    //append the responseData used in connectionDidFinishLoading:
    _responseData = [[NSMutableData alloc] initWithCapacity:contentLength];}

As data is received from the connection we append all the data to the responseData object. It is possible that this method is called more than once; that is why it appends the data instead of setting the data. See listing 3.

Listing 3. Attain all the response data
////////////////////////////////////////////////////////////////////////////////////
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
	[_responseData appendData:data];
	}

After the connection has finished loading, the delegate object method, connectionDidFinishLoading, is called. You now have all the data returned from the request and send that data to the parser so that you can get the blog entry titles in the updates feed.

Because you are done receiving data from the server at this point, your request is complete and you can turn off the network activity indicator, as listing 4 demonstrates.

Listing 4. Create the parser and pass it the data to parse
////////////////////////////////////////////////////////////////////////////////////
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

	[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
		
		
		AtomParser *parser = [[AtomParser alloc] initWithData:_responseData];
		[parser parse];
				
		_items = parser.items;
		[_tableView reloadData];
}

Finally, you create a class that parses the Atom feed. First, create an Objective-C class in your XCode project. We named our Atom feed parser AtomParser.m and AtomParser.h. Then change it so that AtomParser.h is a subclass of NSXMLParser and also implements the NSXMLParserDelegate protocol. This step is done in AtomParser.h and looks something like this:

@interface AtomParser : NSXMLParser <NSXMLParserDelegate>

Now that you have the AtomParser class created, you need to create the parse method that is called in listing 5, which shows what is contained in the parse method in AtomParser.m.

Listing 5. Invoke the NSXMLParser
- (BOOL)parse {
	_items = [[NSMutableArray alloc] init];
	
	self.delegate = self;
	
	BOOL result = [super parse];
	
	
	return result;
}

Now, you need to initialize the items array, which is where you store all the information that you will to pass back to SampleApplicationViewController.m to be displayed in the tableView. Set the delegate to self, which means you have to define the NSXMLParserDelegate methods:

  • elements: parser:didStartElement:namespaceURI:qualifiedName:attributes:
  • parser:foundCharacters:
  • parser:didEndElement:namespaceURI:qualifiedNames:

After that, call the inherited superclass of the AtomParser class to run the parse of the data returned from the Lotus Connections API. As the data is parsed, the delegate methods are called; this step is where you decide which data to ignore and which data to put into the items array. For the sample application, you are interested only in the title elements in an <entry> tag. Later we show you how to display this information in a UITableView.

After the NSXMLParser method parse starts to run, it first calls the delegate implementation of method : parser:didStartElement:namespaceURI:qualifiedName:attributes:. This method looks at the element names and looks for a <title> element. When it finds one, it sets BOOL _foundTitle to YES so that it knows to store the string contents in the <title> element. You use the <title> element in this example because the tableView displays all the titles of the last 30 blog entries from the API data that you retrieved. Listing 6 shows how to see if the elementName is equal to the title;bif so, it sets the foundEntry BOOL appropriately. It also checks the elementName for the string “entry” to verify that all the tags in the Atom feed that are called “title” are children of <entry> tags. In this case, you want only <title> elements that are children of <entry> elements. If you look at the entire Atom feed returned by the API, you can see that there is a <title> element that defines the title of the Atom feed. This <title> element is not a child of an <entry> element; therefore we will ignore it.

Listing 6. Check the elementNames
////////////////////////////////////////////////////////////////////////////////////
- (void)         parser: (NSXMLParser*) parser
    didStartElement: (NSString*) elementName
        namespaceURI: (NSString*) namespaceURI
    qualifiedName: (NSString*) qName
        attributes: (NSDictionary*)attributeDict {
        if ([elementName isEqualToString:@"entry"]) {
            _foundEntry = YES;
        }
        if ([elementName isEqualToString:@"title"]) {
            _foundTitle = YES;
            _title = [[NSMutableString alloc] init];
        }
	
}

The next method called in the parse process is parser:foundCharacters:, which provides all or part of the string content in the current element. Because there is no guarantee that it calls all the string content the first time, the NSMutableString _title object appends the current string until the last method is called:

parser:didEndElement:namespaceURI:qualifiedNames:

Listing 7. Save the text value of the title element
////////////////////////////////////////////////////////////////////////////////////
- (void)         parser: (NSXMLParser*) parser
        foundCharacters: (NSString*) string {
        	if (_foundTitle '&& _foundEntry) {
        			[_title appendString:string];
        	}
}

The last method called as the delegate of NSXMLParserDelegate is parser:didEndElement:namespaceURI: qualifiedName:. This method checks to see if the elementName is equal to “title” and if _foundEntry BOOL is set to YES. If elementName is equal to “title” and _foundEntry is true, then you have parsed the entire title tag so save the string value for the contents of the <title> elment in the items array. This step means that we have found the end of a <title> element that is a child of a <entry> element. If this is true, then it saves the string value for the contents of the <title> element in the items array. You appended the string values to _title in listing 7. After the title string is added to the items array, you release the title string object so that the next title is not appended to the previous title. If elementName is equal to “entry”, then you want to set foundEntry to NO. In other words, if the parser finds an element with the name “title”, the parser knows that it is not a child of an <entry> element, and all other element names in this example are ignored.

Listing 8. Save the title element text value
////////////////////////////////////////////////////////////////////////////////////
- (void)         parser: (NSXMLParser*) parser
    didEndElement: (NSString*) elementName
      namespaceURI: (NSString*) namespaceURI
    qualifiedName: (NSString*) qName {
      if ([elementName isEqualToString:@"title"] && _foundEntry) {
            _foundTitle = NO;
            [_items addObject:_title];
            [_title release];
      }
      if ([elementName isEqualToString:@"entry"]) {
            _foundEntry = NO;	
      }
}

Displaying the data on the iPhone

Finally, you want to prepare the data to display on the iPhone as you intend the user to view it.

When the parsing is complete, you have an array of NSString items ready to be displayed in the table. To make the tableview, load the table data in items array call [_tableView reloadData] shown in listing 4. This code calls the tableview delegate method implementation of tableView:cellForRowAtIndexPath:. Because SampleApplicationViewController.m is the tableView delegate, you call the method defined in that class. You can see the tableView delegate and data source set below in the viewDidLoad method. This code is inserted just above the NSURLRequest and NSURLConnection code.

Listing 9. Implement viewDidLoad
// Implement viewDidLoad to do additional setup after 
loading the view, typically from a nib.

- (void)viewDidLoad {

    [super viewDidLoad];


_tableView = [[[UITableView alloc] initWithFrame:self.view.bounds] autorelease];

    [self.view addSubview:_tableView];


_tableView.delegate = self;

_tableView.dataSource = self;


//Blogs Atom feed API

NSURL *url = [[[NSURL alloc] 

initWithString:@"https://www.ibm.com/developerworks/mydeveloperworks/news/atom/
stories/public?source=blogs&ps=30"]

  autorelease];


NSURLRequest* request = [[NSURLRequest alloc] 

initWithURL:url];

When the UITableView is loading, it calls tableView:cellForRowAtIndexPath: as many times as the table cells are viewable on the screen. For example, if the screen is 100 pixels tall and each table cell is 10 pixels tall, then it calls the method 10 times. The method returns a TableViewCell class instance. This instance is a layout of the data displayed in the table. As the user scrolls through the table, it calls tableView:cellForRowAtIndexPath: every time a table cell shows on the screen. Each time the UiTableView calls the method, it passes an indexPath, which is the value of the UITableViewCell it wants returned. In the sample application, you pass back a UITableViewCell with the title of the blog entry set in the textLabel of the UITableViewCell.

Listing 10. Create UITableViewCell's
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)
indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
 		    NSString* title = [[NSString alloc] initWithString:[_items
objectAtIndex:indexPath.row]];
            cell.textLabel.font = [UIFont systemFontOfSize:12];
            cell.textLabel.text = title;
                     				 				
    }
    // Configure the cell...
    return cell;
  }

Finally, the UiTableView needs to know how many rows to display in the table. You need to display only the number of table rows that are in the items array. Because the API call requested 30 entries, the item array size is 30. Just to be sure, the table view data source class implements tableView: numberOfRowsInSection:. This method returns the size of the tableView based on the size of the items array, which in this case is 30.

Listing 11. Get the number of rows and sections in the UITableView
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)
section {
	return [_items count];
	}

The sample application does not include a detail view accessed when the user selects a UITableViewCell as the My developerWorks application does; however, this task can be done by implementing tableView:didSelectRowAtIndexPath:. In this method, you initialize another UIViewController and have the application navigate to that view, showing a more detailed view of the UITableViewCell.

Congratulations! After completing these steps your sample iPhone application displaying the 30 most recent blog entries from Lotus Connections looks like the display shown in figure 2.

Figure 2. Windows Picture and Fax Viewer
Windows Picture and Fax Viewer

Tuning the performance of your application

When you use the Lotus Connections Atom APIs, sometimes you might want to consider implementing a local cache to optimize the performance of your iPhone application, depending on what your application displays. Here’s what you should think about:

  • If your application is accessing data that isn’t updated often, such as a user’s profile, you should implement caching.
  • If you are getting data that updates often, such as the data in the blogs sample application, caching is not ideal because it does not display the most up-to-date user data.

Caching can be implemented at several different levels. You can cache the results in the NSMutableData returned from the connection request. This approach saves the application from downloading the feed every time it is loaded. The application is still required to parse the cached data before it displays it.

Alternatively, you can cache the parsed data that is in the items array. This implementation eliminates the need to download the feed and parse it before displaying it.

To cache the items array, the objects stored in the array need to conform to NSCoding. In the sample application, the items array stores the title as NSString objects; therefore, it can be cached to disk in the application.

Listing 12. Set and Get cached NSArray items
////////////////////////////////////////////////////////////////////////////////////
+(void)cacheItems:(NSArray*)items {
	NSString *kFile = @"cachedArrayFile";
	NSString *kArray = @"Array";
		
	NSMutableData *theData;
	NSKeyedArchiver *encoder;
	
	theData = [NSMutableData data];
	encoder = [[NSKeyedArchiver alloc]
initForWritingWithMutableData:theData];
	[encoder encodeObject:items forKey:kArray];
	[encoder finishEncoding];
	
	NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDirectory = [paths objectAtIndex:0];
	NSString *path = [documentsDirectory
stringByAppendingPathComponent:kFile];

	[theData writeToFile:path atomically:YES];
	[encoder release];
}


////////////////////////////////////////////////////////////////////////////////////
+(NSMutableArray *)getCachedItems {
	NSString *kFile = @"cachedArrayFile";
	NSString *kArray = @"Array";
	NSMutableArray *tempArray = [NSMutableArray array];
	NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *documentsDirectory = [paths objectAtIndex:0];
	NSString *path = [documentsDirectory
stringByAppendingPathComponent:kFile];

	NSFileManager *fileManager = [NSFileManager defaultManager];
	if([fileManager fileExistsAtPath:path]) {
		//open it and read it
		NSMutableData *decodeData;
		NSKeyedUnarchiver *decoder;
		
		decodeData = [NSData dataWithContentsOfFile:path];
		decoder = [[NSKeyedUnarchiver alloc]
		
initForReadingWithData:decodeData];
		tempArray = [decoder decodeObjectForKey:kArray];
		[decoder finishDecoding];
		[decoder release];
	}
	return tempArray;
}

Conclusion

Using the Lotus Connections 2.5 APIs to create applications for the iPhone is straightforward. As long as you have a basic understanding of how to navigate the Lotus Connections APIs and are able to follow some simple steps to attain the data from the Lotus Connections Atom feeds, parse the data, and then display it within your application, you are well on your way. To discover all that Lotus Connections 2.5 APIs are capable of, check out the My developerWorks iPhone application available now in the Apple aApp Store.


Download

DescriptionNameSize
Code sampleSampleApplication.zip39.5KB

Resources

Learn

Get products and technologies

Discuss

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 IBM collaboration and social software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Lotus
ArticleID=524958
ArticleTitle=Developing a social networking iPhone application based on IBM Lotus Connections 2.5
publish-date=10122010