开发基于 IBM Lotus Connections 2.5 的社交网络 iPhone 应用程序

时间:2011年03月14日 14:07:12 来源:IBMdeveloperWorks 作者:Nicholas Poore, 顾问软件工程师, IBM

简介

developerWorks 认识到移动设备已成为首选的沟通和内容聚合模式,尤其是 Apple iPhone,在全世界广受欢迎。此外,IBM Lotus® Connections 2.5,My developerWorks 社区的底层支持产品,提供了最佳的用户体验,能模拟最流行的社交网络应用程序。

developerWorks iPhone 应用程序 突出应用了 Lotus Connections 的很多流行社交网络特性:

  • 公共更新视图,允许用户查看社区正在发生的所有活动,通过标记、关键字或用户名搜索,找到 My developerWorks 内容和用户。
  • 通过 “我的更新” 来保持实时更新的功能,向配置文件中加入状态,或在伙伴的配置文件中添加评论。

所有这些特性都通过使用 Lotus Connections 2.5 公共 APIs 完成。

作为对 Lotus Connections 2.5 APIs 的简介,我们将示范创建一个简单的 iPhone 应用程序的步骤,该程序显示 My developerWorks 上最近的博客条目。此过程需要我们了解如何访问博客 API,从提要下载数据,解析数据,并在 iPhone 上正确显示数据。


Lotus Connections 2.5 API 导航

Lotus Connections 提供了数量庞大的公共应用程序编程接口(APIs)用于开发。理解如何根据您的开发需求使用这些 APIs,无论是用于 iPhone 应用程序或其他用途,第一步都是要知道如何查阅 API 文档。

Lotus Connections 应用程序(Activities、Blogs、Bookmarks、Communities、Files、Profiles、Wikis)和 Lotus Connections 主页都提供 APIs,让您可以将其与其他应用程序,如 iPhone 应用程序集成。使用这些 APIs,您就可以通过编程方式访问和利用您在 Lotus Connections 社区套件中所看到的数据,如 My developerWorks 上所有的社区活动。Lotus Connections APIs 基于 Atom Syndication Format,它允许两种通信方式(读/写)。要使用这些 APIs,您使用的编程语言只要能通过 HTTP 发送和接收 XML 就行了。

Lotus Connections APIs 在 Lotus Connections 信息中心 有详细说明。对于 iPhone 应用程序,重点是获得用户对社区持续的更新,因此我们找到名为 “Getting a feed of all our public updates” 的文档。该文档指导我们使用 Updates APIs。Updates APIs 能让我们获得最近的 My developerWorks 生成的最近的(公共)活动。我们在 Blog 样例中也引用同样的 Updates APIs。要找到这些 APIs,按如下顺序 Developing - Lotus Connections APIs - Updates API - Getting updates feeds - Getting a feed of all public updates。

现在,最重要的是理解如何使用这个提要来实现您自己的应用程序的提要。

表 1 列出了用来从 API 获取内容的资源。资源是让您能访问数据的 API 中的 URL。该资源提供了访问 Lotus Connections 中所有公共更新的根 URL。


表 1. Atom API 请求细节

资源 描述 /atom/stories/public
获得所有公共更新的提要

 

该 URL 检索所有公共更新;本例中,我们只需要一个应用程序 Blogs,作为我们返回时的提要。

表 2 描述了参数,例如确定一个应用程序 Blogs,可以用来允许应用程序检索所需的过滤后的内容。


表 2. 过滤 Lotus Connections 提要的参数

参数 描述 before container email lang page ps since source userid
只返回参数指定日期前最后一次修改的内容。使用如下语法格式化日期:2008-02-07T21:07:56Z。
以下条目的惟一 ID :

  • Activities 应用程序中的活动
  • Blogs 应用程序中的博客
  • Bookmarks 应用程序中某人的书签收藏
  • Communities 应用程序中的社区
  • Files 应用程序中的文件
  • Profiles 应用程序中某人的配置文件
  • Wikis 应用程序中的维基

与源参数一起使用该参数。在源参数中,指定在哪个应用程序中,该惟一 ID 的条目有效。如果源参数中未指定有效的应用程序名,参数将被忽略。

某人的 Email 地址。使用这个参数来限制更新的提要,用来包含某人的更新。如果 Lotus Connections 配置为隐藏 email 地址,则不要使用此参数;使用用户 ID。
使用该参数以不同于 HTTP 请求中指定的语言请求内容。 使用以下格式指定语言:

  • Java™ 格式(例如,en_IE,es_GT)
  • Dojo 格式(例如 en-ie, es-gt)
页数。指定返回的页数。默认是 1。
页面大小。指定每个页面返回的条目数。允许的最大数是 100。
只返回最后一次修改位于该参数指定的日期之后的内容。使用以下语法格式化日期:2008-02-07T21:07:56Z。
指定只返回某一特性的提要。选项有:

  • Activities
  • Blogs
  • Bookmarks
  • Communities
  • Files
  • Profiles
  • Wikis
某人的惟一 ID。使用该参数限制更新提要,仅包含特定的某人生成的更新。当同时指定该参数和 email 参数时,优先采用该参数值。

 

我们感兴趣的是 30 个最近的博客更新,而不是默认设置的 20 个条目。我们使用页面大小参数(ps)来实现这项设计。

查看最近 30 条博客更新的提要是这样:

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

其他常用的参数有:languagesince 和 userid。要注意,语言类型返回参数不翻译用户输入的内容;它翻译标准标签或与此内容相关的 UI。before 和 since 参数可让您运用时间戳进行限制,只检索某一特定日期之前和之后的数据。Userid 可用来限制特定的用户产生的更新。

尽管为了举例,我们只演示了一条提要,但 My developerWorks iPhone 应用程序可以利用 Lotus Connections APIs 显示所有用户输入的内容,由应用程序过滤,搜索结果,配置数据等等。


使用 Lotus Connections APIs 获取应用程序数据并解析

现在已经了解如何使用 APIs 访问 Lotus Connections 数据,下一步就是开始编写 iPhone 应用程序。我们使用 Apple xCode 模板生成器来创建一个名为 SampleApplication 的 View-based Application iPhone 项目(见图 1)。


图 1. New xCode Project 窗口
New xCode Project 窗口 
 

项目初始化完成后,会自动创建若干文件,包括 SampleApplicationViewController.h 和 SampleApplicationViewController.m 文件。在 SampleapplicationViewController.m 文件中可以看到 viewDidLoad 方法。应用程序中加载视图时会调用此方法。将对 Lotus Connections APIs 提要的连接请求加入 viewDidLoad 方法表示,当视图加载并且用户启动应用程序时,请求 API。要使用 API,首先要定义一个 NSURLRequest,在其中加入在第一节中获取的 ATOM 提要的 URL 。

有了 NSURLRequest 对象之后,创建一个 NSURLConnection,将 NSURLRequest 传递给它并给它提供委托。NSURLConnection 是一个简单的接口,通过使用 Atom APIs 在 iPhone 应用程序和 Lotus Connections 之间实现通信。使用 NSURLConnection 创建连接很简单。它需要一个其中包含一个委托对象,并且此委托对象实现以下方法的应用程序:

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

我们给它提供一个委托,因为 NSURLConnection 异步加载请求,并需要知道如何处理从 NSURLConnection 返回的 NSData。

在进行下一步工作之前,我们根据 Apple 开发指导定义一个委托对象。委托对象或者说委托方法是一个简单且功能强大的模式,在此模式中,程序中的一个对象,其行为代表另一对象,或与其一致。委托对象保持对另一个对象的引用,委托方法在恰当的时间向其发送消息。消息通知事件的委托方法,委托对象将要处理或已经处理。本例中,当数据从 NSURLConnection 中返回,它调用委托对象并通知它数据已准备好被解析并可以显示给用户。

清单 1 显示了应用程序示例中用 NSURLRequest 初始化连接的代码。如果连接成功,就将 networkActivityIndicatorVisible 设为 YES。如此设置就可以在 iPhone UI 上显示一个可见的指示器,表示有网络活动正在进行。


清单 1. 创建 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	
}

 

 

下一步是定义连接响应时要调用的方法。此方法中,我们从响应对象的头部获取 Content-Length。我们使用这个值来定义 NSMutableData 对象的大小,此对象用来存储从请求中返回的数据。见清单 2。


清单 2. 处理 NSURLConnection 响应

////////////////////////////////////////////////////////////////////////////////////
- (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];}

 

 

当从连接接收到数据后,将所有数据添加到 responseData 对象中。此方法有可能不止一次被调用;这就是我们添加数据而不是设置数据的原因。见清单 3。


清单 3. 获取响应数据

////////////////////////////////////////////////////////////////////////////////////
- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
	[_responseData appendData:data];
	}

 

 

连接完成加载后,调用委托对象方法 connectionDidFinishLoading。现在已从请求返回所有数据,并将数据发送给了解析器,以便能获得更新提要中的博客条目标题。

由于现在已经从服务器接收完数据,请求就完成了,可以关闭网络活动指示器了,如清单 4 所示。


清单 4. 创建解析器并发送数据以便解析

////////////////////////////////////////////////////////////////////////////////////
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

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

 

 

最后,创建一个解析 Atom 提要的类。首先在 XCode 项目中创建一个 Objective-C 类。将 Atom 提要解析器命名为 AtomParser.m 和 AtomParser.h。然后进行修改,让 AtomParser.h 成为 NSXMLParser 的子类,并实现 NSXMLParserDelegate 协议。这在 AtomParser.h 中进行,完成后像这样:

@interface AtomParser : NSXMLParser <NSXMLParserDelegate>

现在已创建了 AtomParser 类,现在需要创建清单 5 中调用的解析方法,该清单下显示了 AtomParser.m 中的解析方法所包含的内容。


清单 5. 调用 NSXMLParser

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

 

 

现在需要初始化条目数组,它用来存储所有将会传回给 SampleApplicationViewController.m 并在 tableView 中显示的数据。将委托方法设置为自身,这意味着您要自己定义 NSXMLParserDelegate 方法:

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

然后,调用 AtomParser 类的继承子类来解析从 Lotus Connections API 返回的数据。当解析数据时,会调用委托方法;这一步中,要决定哪些数据要忽略,哪些数据放入条目数组中。本应用程序示例中,我们只关心 <entry> 标记中的标题元素。稍后我们将演示如何在 UITableView 中显示信息。

NSXMLParser 方法 parse 开始运行后,它首先调用方法的委托实现:parser:didStartElement:namespaceURI:qualifiedName:attributes:。此方法根据元素名查找 <title> 元素。它找到一个后,将 BOOL _foundTitle 设置为 YES,以便在 <title> 元素中存储字符串内容。本例中使用 <title> 元素,因为 tableView 会显示所检索到的 API 数据中的最近 30 条博客将记录。清单 6 显示如何确定 elementName 是否与标题相同;如果不同,它会将 foundEntry 设置为 BOOL。它还会检查 elementName 中是否有 “entry” 字符串,以确认所有名为 “title” 的 Atom 提要中的所有标记都是 <entry> 标记的子元素。本例中,只需要 <entry> 子元素中的 <title> 元素。如果看一看从 API 返回的所有 Atom 提要,会看到有一个 <title> 元素定义了 Atom 提要的标题。这个 <title> 元素不是 <entry> 的子元素;因此将其忽略。


清单 6. 检查 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];
        }
	
}

 

 

解析过程中下一个调用的方法是 parser:foundCharacters:,它提供了当前元素中全部或部分的字符串内容。由于不能保证第一次就调用所有字符串内容,因此在最后一个方法调用之前,NSMutableString _title 对象会一直添加当前字符串:

parser:didEndElement:namespaceURI:qualifiedNames: 


清单 7. 保存标题元素的文本值

////////////////////////////////////////////////////////////////////////////////////
- (void)         parser: (NSXMLParser*) parser
        foundCharacters: (NSString*) string {
        	if (_foundTitle '&& _foundEntry) {
        			[_title appendString:string];
        	}
}

 

 

最后一个要调用的 NSXMLParserDelegate 委托方法是 parser:didEndElement:namespaceURI: qualifiedName:。该方法检查 elementName 是否等于 “title”,以及 _foundEntry BOOL 是否设置为 YES。如果 elementName 等于 “title” 并且 _foundEntry 为 true,那就已经解析了整个标题标记,保存条目数组中的 <title> 元素内容中的字符串值。这步表示,我们已经找到作为 <entry> 的子元素的 <title> 元素的结尾。如果是这样,它就将字符串值保存为条目数组中 <title> 元素的内容。在清单 7 中将字符串值添加到 _title 中。标题字符串添加到条目数组后,就释放标题字符串对象,这样下一个标题就不会添加到前一个标题上。如果 elementName 不等于 “entry”,那就要将 foundEntry 设成 NO。换句话说,如果解析器找到名为 “title” 的元素,解析器就知道它不是 <entry> 元素的子元素,本例中其他元素名就被忽略。


清单 8. 保存标题元素文本值

////////////////////////////////////////////////////////////////////////////////////
- (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;	
      }
}

 

 


在 iPhone 上显示数据

最后,要准备好数据,让它显示在 iPhone 上希望用户看到的地方。

当解析完成后,就有了可在表格中显示的 NSString 条目数组。要实现 tableview,加载条目数组调用 [_tableView reloadData] 中的表格数据,如清单 4 中所示。这段代码调用了tableview 委托方法实现 tableView:cellForRowAtIndexPath:。由于 SampleApplicationViewController.m 是 tableView 委托方法,因此可以调用该类中定义的方法。可以看到在 viewDidLoad 方法中设置了 tableView 委托和数据源。这段代码在 NSURLRequest 和 NSURLConnection 代码之前。


清单 9. 实现 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];

 

 

加载 UITableView 时,它调用 tableView:cellForRowAtIndexPath: 的次数等于屏幕上可见单元格的个数。例如,如果屏幕高度是 100 个像素,每个单元格高度是 10 个像素,那么它调用了 10 次该方法。该方法返回一个 TableViewCell 类实例。该实例是表格中显示的数据的布局。当用户滚动表格时,每当有单元格显示在屏幕上,就会调用 tableView:cellForRowAtIndexPath:。每次 UiTableView 调用该方法时,它都会传递一个 indexPath,这是它要返回的 UITableViewCell 值。在此应用程序示例中,传回了一个 UITableViewCell,它带有在 UITableViewCell 中的 textLabel 设置的博客条目标题。


清单 10. 创建 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;
  }

 

 

最后,UiTableView 需要知道在表格中显示多少行。您只要显示条目数组中的表格行数即可。由于 API 调用请求 30 条,条目数组大小就是 30。只要确保表格视图数据源实现了 tableView: numberOfRowsInSection:。该方法返回了基于数组大小的 tableView 的大小,本例中是 30。


清单 11. 获取 UITableView 中的行数和段数 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
	return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)
section {
	return [_items count];
	}

 

 

这个应用程序示例未包含当用户选择 UITableViewCell 时访问的明细视图,就像 My developerWorks 应用程序那样;但是可以通过实现 tableView:didSelectRowAtIndexPath: 来完成这项任务。在此方法中,初始化另一个 UIViewController 并让应用程序导航到这个视图,从而显示一个 UITableViewCell 的明细视图。

恭喜!经过这一系列步骤之后,您的 iPhone 应用程序示例已经能够显示来自 Lotus Connections 的最新 30 条博客,它应该像图 2 这样。


图 2. Windows Picture 和 Fax Viewer
Windows Picture 和 Fax Viewer 
 


调整应用程序性能

当使用 Lotus Connections Atom APIs 的时候,您可能会想要实现一个本地缓存,以优化 iPhone 应用程序性能,这取决于您的应用程序要显示什么。 以下是需要考虑的问题:

  • 如果您的应用程序访问的数据不常更新,例如用户的配置文件,那就应该实现缓存。
  • 如果获取的数据经常更新,如博客应用程序示例中的数据,就没必要用缓存,因为它不会显示最新的用户数据。

可以实现不同级别的缓存。可以缓存从连接请求返回的 NSMutableData 中的结果。这种方法可以节省应用程序每次加载时下载提要的时间。该应用程序仍需要每次在显示缓存数据前解析。

或者,您可以缓存条目数组中已解析的数据。这种方法就不需要下载摘要并在显示前解析。

要缓存条目数组,存储在数组中的对象要遵守 NSCoding。在应用程序示例中,条目数组将标题保存为 NSString 对象;因此,它能在应用程序中被缓存到磁盘上。


清单 12. 设置和获取缓存的 NSArray 条目

////////////////////////////////////////////////////////////////////////////////////
+(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;
}

 

 


结束语

使用 Lotus Connections 2.5 APIs 创建 iPhone 应用程序很简单。只要对如何导航 Lotus Connections APIs 有基本了解,就可以遵循一些简单的步骤,从 Lotus Connections Atom 摘要中获取数据、解析数据,然后显示在您的应用程序中,您一定能做到。要了解有哪些 Lotus Connections 2.5 APIs 可用,请立即查看 Apple App Store 中的 My developerWorks iPhone 应用程序。

你可能感兴趣的:(Connection)