为SharePoint 2013开发iPhone客户端应用程序





5.00/5 (7投票s)
如何编写 iPhone 应用程序以向 SharePoint 进行身份验证并读取 SharePoint REST API 提供的列表内容。
引言
欢迎大家。在本文中,我将讨论如何为 Microsoft SharePoint 2013 开发 iPhone 客户端。
我们的应用程序将向 SharePoint 进行身份验证并读取 SharePoint REST API 提供的列表内容。我们开始吧。
准备环境
关于 SharePoint 环境的部署,您可以采用任何舒适的方式。您可以在自己的服务器上部署 SharePoint 2013,或者将 SharePoint 作为部署在 Windows Azure 或其他云(例如 http://cloudshare.com/)中的虚拟机。我有一个使用 Office 365 的 SharePoint Online 的测试环境。
我们的移动应用程序将显示照片列表,因此请为此列表添加测试数据。在浏览器中打开站点集合中的一个站点,该站点将包含照片列表。在我的情况下,它是 https://dmitrykomin-public.sharepoint.com/。在管理面板中单击“站点设置”链接。
然后,在右侧窗格中,单击“站点内容”链接。
“站点内容”页面打开,其中显示了站点列表等内容。单击“照片”列表。
向列表中添加一些照片。
身份验证
SharePoint 2013 支持基于声明的身份验证,这意味着您可以配置 SharePoint 2013,以便用户可以通过各种身份提供者进行身份验证:Facebook、Google、Windows Live ID、Yahoo 等。基于声明的身份验证机制基于这样一个事实,即所有与身份验证相关的工作实际上都由身份提供者执行。成功身份验证后,用户会从安全令牌服务获取特定安全令牌以换取其凭据。然后,此令牌用于访问检查令牌并根据令牌获取声明的应用程序,也就是说,有关特定用户的信息对于每个身份提供者都是特定的。
有关基于声明的身份验证的更多信息在此处:http://social.technet.microsoft.com/wiki/contents/articles/14214.sharepoint-2013-claims-based-authentication.aspx。SharePoint 中此身份验证机制的特点是,所有这些获取安全令牌的操作都是基于 HTTP 重定向自动完成的,这些重定向由网络浏览器执行。安全令牌存储在浏览器 cookie 中。但是,如果我们要将 iPhone 用作 SharePoint 的移动客户端应用程序,我们该怎么办?很简单——我们使用 UIWebView 类在 iPhone 应用程序中创建浏览器视图,并在浏览器中验证用户以获取安全令牌,然后它允许直接与 SharePoint REST API 通信,传递从 HTTP 请求的 cookie 中获取的安全令牌。(技术上可以通过不使用 UIWebView 完全模拟浏览器行为来编写身份验证代码,自己处理 HTTP 重定向,创建自己的视图以输入用户凭据等。与基于浏览器的解决方案相比,这种方法非常困难。如果出于某种原因需要此身份验证方法,请告诉我,我将在未来的文章中讨论此方法。)现在让我们开始开发移动应用程序。启动开发环境 Xcode。选择“创建新的 Xcode 项目”。作为新项目模板,选择“单视图应用程序”。
指定项目名称,例如 SPConnectTest。为身份验证创建控制器。运行命令 File->New->File… 作为新模板文件,选择 Objective-C 类。
指定新类 LoginViewController 的名称,在 Subclass of 中选择 UIViewController。打开文件 LoginViewController.h 并输入以下代码
@interface LoginViewController : UIViewController
{
IBOutlet UIWebView * webView;
}
@property (nonatomic, retain) IBOutlet UIWebView *webView;
- (void)loadSharePointList : (NSHTTPCookie *) rtFaCookie : (NSHTTPCookie *) fedAuthCookie;
- (void)verifyCookies;
@end
现在打开 MainStoryboard_iPhone.storyboard
。Storyboard 编辑器打开。在右侧窗格中,在“对象”部分找到 Web View 组件并将其拖动到现有(它显示为 iPhone 屏幕)。
在 View Controller Scene 中选择 View Controller。在右侧窗格的 Class 框中,选择 LoginViewController。
现在右键单击 View Controller,找到我们的变量 webView,然后将链接从它拖到 Storyboard 编辑器工作区中的 Web 视图。
打开 LoginViewController.m 文件。添加以下代码
@synthesize webView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView.delegate = self;
NSString* authUrl = @"https://dmitrykomin-public.sharepoint.com/_layouts/15/authenticate.aspx?Source=/";
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: authUrl]]];
}
-(void)webViewDidStartLoad:(UIWebView *)webView {
[self verifyCookies];
}
-(void)webViewDidFinishLoad:(UIWebView *)webView {
[self verifyCookies];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)verifyCookies
{
__weak NSHTTPCookie * rtFaCookie = nil;
__weak NSHTTPCookie * fedAuthCookie = nil;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookiesArray = [storage cookies];
for (NSHTTPCookie *cookie in cookiesArray) {
if ([[cookie name] isEqualToString:@"FedAuth"]) {
fedAuthCookie = cookie;
continue;
}
if ([[cookie name] isEqualToString:@"rtFa"]) {
rtFaCookie = cookie;
continue;
}
}
if(rtFaCookie != nil && fedAuthCookie != nil){
[self loadSharePointList : rtFaCookie : fedAuthCookie];
}
}
- (void)loadSharePointList : (NSHTTPCookie *) rtFaCookie : (NSHTTPCookie *) fedAuthCookie
{
}
@end
现在来看看代码做了什么。在 viewDidLoad 处理程序中,它在加载视图时调用,我们执行对我们网站的身份验证 URL 的 Web 请求。在用户输入其凭据后,将执行一系列 HTTP 重定向,每次 HTTP 重定向后都会调用 webViewDidStartLoad
webViewDidFinishLoad
方法。在这些方法中,我们调用 verifyCookies,它检查 cookie 中的安全令牌(“rtFa”和“FedAuth”cookie)。如果获取到 securityToken,我们调用 loadSharePointList,它将来会加载我们的 SharePoint 列表的内容。
是时候运行我们的应用程序了。点击“运行”。如果一切正确完成,您应该在 iOS 模拟器中看到以下内容
在 loadSharePointList 方法中设置断点,然后切换到 iOS 模拟器窗口,输入用户凭据,然后单击“登录”链接。片刻之后,您将进入 loadSharePointList
方法。因此,我们已成功在 SharePoint 中实现身份验证。
从 REST API 接收数据
让我们强调与 SharePoint 2013 REST API 通信的主要点,因为我无法显示所有代码。您可以通过下载本文的演示示例来查看它。为了与 SharePoint 2013 REST API 通信,我编写了一个类 SPRestClientRequest
。这是代码
SPRestClientRequest.h:
@interface SPRestClientRequest : NSObject
{
__weak NSHTTPCookie * rtFaCookie;
__weak NSHTTPCookie * fedAuthCookie;
}
- (id) initWithCookies : (NSHTTPCookie *) rtFa : (NSHTTPCookie *) fedAuth;
- (NSDictionary *) sendRequest : (NSString*) requestUrl;
@end
SPRestClientRequest.m:
@implementation SPRestClientRequest
- (id) initWithCookies : (NSHTTPCookie *) rtFa : (NSHTTPCookie *) fedAuth
{
if(self = [super init])
{
rtFaCookie = rtFa;
fedAuthCookie = fedAuth;
}
return self;
}
- (NSDictionary *) sendRequest : (NSString*) requestUrl
{
NSURL *url = [NSURL URLWithString:requestUrl];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
NSArray* cookieArray = [NSArray arrayWithObjects: rtFaCookie, fedAuthCookie, nil];
NSDictionary * cookieHeaders = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray];
NSMutableDictionary * requestHeaders = [[NSMutableDictionary alloc] initWithDictionary: cookieHeaders];
[requestHeaders setObject: @"application/json;odata=verbose" forKey: @"Accept"];
[theRequest setHTTPMethod:@"GET"];
[theRequest setAllHTTPHeaderFields:requestHeaders];
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];
if (data) {
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", jsonString);
return [jsonString JSONValue];
}
return nil;
}
@end
如您所见,sendRequest 方法生成 HTTP 请求,执行它并以 JSON 形式返回结果。
除了这个类,我还编写了一些类,它们创建了一个简单的对象模型来使用 API:SPListCollection
、SPList
、SPListItem
。例如,这是 SPListCollection
的代码
SPListCollection.h:
@interface SPListCollection : SPRestClientRequest
- (NSDictionary *) getAllItems;
- (NSDictionary *) getItemByEntityType : (NSString *) entityType;
@end
SPListCollection.m:
@implementation SPListCollection
- (NSDictionary *) getAllItems
{
NSDictionary * jsonValue = [super sendRequest:@"https://dmitrykomin-public.sharepoint.com/_api/web/lists"];
return [[jsonValue objectForKey:@"d"] objectForKey:@"results"];
}
- (NSDictionary *) getItemByEntityType : (NSString *) entityType
{
NSDictionary *jsonResults = [self getAllItems];
for (NSDictionary *jsonResult in jsonResults) {
NSString *entityTypeName = [jsonResult objectForKey:@"EntityTypeName"];
if([entityTypeName isEqualToString: entityType]){
return jsonResult;
}
}
return nil;
}
@end
这个类和其他类一样,只是向特定的 SharePoint REST 端点发出请求并解析调用的结果。SharePoint 2013 REST API 的完整文档,我们可以在这里找到:http://msdn.microsoft.com/en-us/library/jj860569.aspx#Reference
从 SharePoint 2013 列表加载照片
现在,有了 REST API 的对象模型,让我们从 SharePoint 2013 列表中加载数据。添加一个名为 PhotosListManager
的新 Objective-C 类。打开 PhotosListManager.h 文件并添加以下代码
@interface PhotosListManager : NSObject
{
__weak NSHTTPCookie * rtFaCookie;
__weak NSHTTPCookie * fedAuthCookie;
}
- (id) initWithCookies : (NSHTTPCookie *) rtFa : (NSHTTPCookie *) fedAuth;
- (NSArray *) loadItems;
@end
打开 PhotosListManager.m 文件并添加以下代码
@implementation PhotosListManager
- (id) initWithCookies : (NSHTTPCookie *) rtFa : (NSHTTPCookie *) fedAuth
{
if(self = [super init])
{
rtFaCookie = rtFa;
fedAuthCookie = fedAuth;
}
return self;
}
- (NSArray *) loadItems
{
NSMutableArray * loadedItems = [[NSMutableArray alloc] init];
SPListCollection * listCollection = [[SPListCollection alloc] initWithCookies : rtFaCookie : fedAuthCookie];
NSDictionary * listMeta = [listCollection getItemByEntityType : @"PhotosList"];
if(listMeta != nil){
NSDictionary * photosMetas = [[[SPList alloc] initWithMetadataAndCookies : listMeta : rtFaCookie : fedAuthCookie] getAllItems];
for (NSDictionary *photoMeta in photosMetas) {
NSDictionary * metadata = [photoMeta objectForKey : @"__metadata"];
NSDictionary * fieldValues = [[[SPListItem alloc] initWithMetadataAndCookies : photoMeta : rtFaCookie : fedAuthCookie] fieldValuesAsText];
if(metadata != nil && fieldValues != nil) {
ListItemDto * listItem = [[ListItemDto alloc] init];
listItem.id = [metadata objectForKey : @"id"];
listItem.uri = [metadata objectForKey : @"uri"];
listItem.type = [metadata objectForKey : @"type"];
listItem.title = [fieldValues objectForKey : @"Title"];
if(listItem.title == nil || [listItem.title isEqualToString : @""]){
listItem.title = [fieldValues objectForKey : @"FileLeafRef"];
}
listItem.created = [photoMeta objectForKey : @"Created"];
NSMutableDictionary * attributes = [[NSMutableDictionary alloc] init];
[attributes setObject: [fieldValues objectForKey : @"FileLeafRef"] forKey: @"FileLeafRef"];
[attributes setObject: [fieldValues objectForKey : @"FileRef"] forKey: @"FileRef"];
NSString * fileUrl = [[AppSettings SiteUrl] stringByAppendingString : [fieldValues objectForKey : @"FileRef"]];
[attributes setObject: fileUrl forKey: @"FileUrl"];
listItem.attributes = attributes;
[loadedItems addObject: listItem];
}
}
}
return loadedItems;
}
@end
我们的管理器类加载照片信息并返回 ListItemDto 列表(充当视图模型)。
在 UITableView 中显示信息
现在我们已经加载了图片,让我们将它们显示在列表中。为此,我们将使用 UITableView 类,更准确地说,它将是基于 UITableView
(UITableViewController
) 的控制器。
添加一个名为 SPListViewController
的新 Objective-C 类。在 Subclass of 框中指定 'UITableViewController
' 类。
打开 SPListViewController.h 文件并添加以下代码
@interface SPListViewController : UITableViewController
{
__weak NSHTTPCookie * rtFaCookie;
__weak NSHTTPCookie * fedAuthCookie;
NSArray * tableData;
}
- (id) initWithCookies : (NSHTTPCookie *) rtFa : (NSHTTPCookie *) fedAuth;
@end
打开 SPListViewController.m 文件并添加以下代码
@implementation SPListViewController
- (id) initWithCookies : (NSHTTPCookie *) rtFa : (NSHTTPCookie *) fedAuth
{
self = [super initWithNibName: nil bundle: nil];
if (self) {
rtFaCookie = rtFa;
fedAuthCookie = fedAuth;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
PhotosListManager * photosListMgr = [[PhotosListManager alloc] initWithCookies: rtFaCookie : fedAuthCookie];
tableData = [photosListMgr loadItems];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [tableData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PhotoItemCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier];
}
ListItemDto * listItem = [tableData objectAtIndex:indexPath.row];
NSString* imageUrl = [listItem.attributes objectForKey : @"FileUrl"];
cell.textLabel.text = listItem.title;
cell.imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]];
return cell;
}
@end
在 viewDidLoad
方法中,我们只需告诉管理器加载照片。在 numberOfRowsInSection
方法中,我们返回该部分中的元素数量(对我们来说是照片数量)。在 CellForRowAtIndexPath
方法中,我们创建一个列表项。在这里,我们只是将照片标题和图像数据分配给 UITableViewCell
项的相应属性。
差不多完成了。剩下要做的只是在 SharePoint 2013 中成功验证后切换到 SPListViewController
。打开 LoginViewController.m 文件并将以下代码添加到 loadSharePointList
方法中
- (void)loadSharePointList : (NSHTTPCookie *) rtFaCookie : (NSHTTPCookie *) fedAuthCookie
{
[[self webView] setHidden:YES];
SPListViewController * listView = [[SPListViewController alloc] initWithCookies: rtFaCookie : fedAuthCookie];
listView.title = @"List Content";
UINavigationController * navigationController = [[UINavigationController alloc] initWithRootViewController:listView];
[self presentViewController : navigationController animated:YES completion: nil];
}
就是这样!运行应用程序。输入用户凭据并点击“登录”按钮后,您应该会看到以下内容
感谢您的阅读。任何评论和建议都将不胜感激。
历史
- 2013 年 4 月 3 日:首次发布。