如何使用Parse为自己的iOS应用创建后台服务
原文在此:http://www.raywenderlich.com/19341/how-to-easily-create-a-web-backend-for-your-apps-with-parse
在上一篇文章中,我们了解了几个比较流行的网络后台服务提供商,以及它们各自的特色。
而在这一篇教程中,我们将了解如何使用Parse来为自己的iOS应用创建后台服务。
这里将使用和上一篇教程中相同的照片分享示例-包括用户登陆,照片上传和照片墙。不过这一次将使用Parse来一步步实现BaaS功能。
为了集中学习parse,我们将使用一个有了用户界面,但还不能上传或下载图片的项目作为基础。
准备好了吗?让我们现在就开始吧
开始前的准备-创建自己的后台服务
在学习如何配置和开发应用前,首先需要在parse的后台创建一个应用。每个开发者的每一款应用都需要一个独一无二的标识符,否则你的数据和账号很有可能和其它人的弄混!
第一步当然是访问www.parse.com并且注册一个账号了。
注册完账号后,会提示你创建第一款应用。需要注意的是,后台的每一款应用都要单独注册。这里我们注册一个名为”tutorialApp”的应用。切换到https://parse.com/apps页面,然后点击create new app,然后输入tutorialApp,就好了。竟然在parse中可能存在很多同名应用,但注册在你名下的使用该名称的应用却只可能有一个。
应用创建完成后,将会自动切换到Dashboard,这里可以查看关于应用的各种信息。
在左侧我们会看到很多信息,包括Application ID,Client Key,…等等。其中Application ID代表该应用在后台的唯一标识,Client Key是用于iOS或者Android客户端代表该应用的标识。其它则分别用于使用javascript,windows,REST API的客户端中。
重点来看看右侧的几个选项卡:
Overview:这里可以看到关于应用的统计信息,比如流量,所发送的推送,调用的API次数等等。
Data Browser:这里可以看到所有上传到后台的数据。同时我们还可以在这里看到用户,并手动管理这些数据,几乎就是一个简单的数据库编辑工具。
Push Notifications:在这里可以向所有的用户推送信息,或者向指定的群体用户推送信息。
Settings:这里可以更改应用的设置,管理安全性,并导出数据。
Parse示例项目
为了集中学习如何创建后台服务,我们需要一个基础项目。
可以从这里直接下载:https://github.com/toniomg/TutorialBase
当然也可以用Github for mac,或者直接在Terminal终端里面输入以下命令:
git clone https://github.com/toniomg/TutorialBase
下载完成后解压缩,并在Xcode中打开,编译运行。首先你会看到一个登陆界面。不过目前没有任何后台服务代码来处理。
在继续学习之前,我们需要打开MainStoryboard.storyboard,并查看应用的结构和流程。
该项目被划分为4 个主视图,每个视图在storyboard中都有自己的视图控制器和视图。
1.
2.
3.
4.
大家可以看看图中应用的流程和界面连接方式:
准备添加Parse后台服务
接下来,很自然的事情就是要配置应用,以便可以绑定Parse的后台服务。
首先要下载parse的框架:
https://parse.com/downloads/ios/parse-library/latest
下载完成后,解压缩并将文件夹Parse.framework拖到项目的Framework文件夹中,注意选中”copy items…”和”create groups…”。
(注意:我第一次添加时Xcode直接崩掉了,如果遇到这种情况,需要在finder里面先删掉parse.framework文件夹,然后重新添加)
默认情况下会将该框架添加到target “tutorialBase”,也就是我们期望的结果。此外,还要确保项目中已经添加了以下框架:
接下来就是在应用开始的时候注册后台服务。
在Xcode中切换到AppDelegate.m,并添加一行代码如下:
#import
接下来在didFinishLaunchingWithOp
[Parse setApplicationId:AppIDclientKey:clientKey];
当然,这里的AppID和clientKey都是Parse提供给我们的。
在浏览器中切换到Parse Dashboard,选择自己的应用,然后拷贝Application ID和Client Key。
这一步完成后,didFinishLaunchingWithOp
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOp
{
// Override point for customization after application launch.
returnYES;
}
此时编译运行应用,确保没有任何编译错误。
如果一切ok,那么恭喜你,我们已经成功的在应用中绑定了Parse 的服务。
接下来就是创建一些示例对象了~
创建示例对象
现在我们的项目已经配置完成,并和Parse连接在一起。这里来复习一下从后台发送和获取对象的方法。
在这个示例中,我们将使用类PFObject,其中提供了基本的对象操作方法,更多关于此类的信息请参考这里:
https://parse.com/docs/ios/api/Classes/PFObject.html
所有上传到Parse的对象都会成为数据库模式中的一个记录。这些对象有点类似于NSMutableDictionarys-我们可以在词典中保持数据,并使用键值来识别,同时也可以在之后使用键值来获取数据。
比如我们要上传一个名为”Player”的对象,以”Name”和”Score”作为字段。这样在我们的数据库中就会有一个名为”Player”的表,同时会保持所有上传的对象。还是举个例子来说明吧。
在AppDelegate.m中找到didFinishLaunchWithOptio
PFObject*player =[PFObjectobjectWithClassN
[playersetObject:@"John"forKey:@"Name"];
[playersetObject:[NSNumber numberWithInt:1230]forKey:@"Score"];//2
[player save];//3
其中第一部分代码创建了一个对象,并指定其类名为Player。
第二部分代码为字段指定数值,比如该player的名字为”John”,分数为”1230”。
第三部分代码用于保存该对象。此时将会以同步方式将对象发送到服务器。
就这么简单,最棒的事情是,你无需在parse的网页界面中手动创建一个数据库表格,parse将根据你所创建的对象来自动创建。
编译运行应用,如果一切正常,那么就可以继续了。
且慢,我们的对象到哪里去了?
为了检查刚才的对象是否已经正确保存,我们可以在浏览器中切换到parse的dashboard,然后点击Data browser选项卡,就可以看到刚才的对象了。
这就是最简单的保存对象的方法。恭喜!你已经成功的和后台服务进行了一次信息交互!
从同步转为异步
可惜此时在控制台中还可以看到警告信息。在对象完全发送完毕前,你的应用将无法使用,因此这是一个同步网络操作!此时不但你无法检查相关的结果,而你的用户也不得不等待API完成工作。
如果是这样,恐怕立马你会获得无数个1星评价。当然,我们有办法来解决这个问题。
将didFinishLaunchWithOptio
PFObject *anotherPlayer = [PFObjectobjectWithClassN
if(succeeded){
NSLog(@"Object Uploaded");
else{
NSString *errorString = [[error userInfo]objectForKey:@"error"];
NSLog(@"Error:%@",errorString);
在上面的代码中,我们使用异步的方式来上传对象,并在块语句中来检查上传结果。现在块语句在iOS中已经越来越普遍了,甚至一些简单的UIView动画都是在块中完成。如果你对块语句比较陌生,可以了解下这篇教程(http://www.raywenderlich.com/9328/creating-a-diner-app-using-blocks-part-1)。
编译运行项目!
然后通过Parse Dashboard页面来查看对象是否已成功发送到服务器。和刚才的区别就在于,在传送对象的同时不会影响应用的其它操作。
如果观察仔细一点的话,你可以已经注意到了设备(或者模拟器)顶部有一个网络活动标识,当登陆界面出现时会看到它在旋转(菊花状。。。)过一会儿当通讯完成时,会看到Xcode的控制台中出现NSLog消息。在上传图片等耗时比较久的对象时,使用异步发送对象无疑会大大提升用户体验。
获取对象
在了解了如何通过parse来保存对象后,现在来看看如何获取对象。Parse提供了一个类PFQuery来执行查询,详细信息可以查看这里:http://www.parse.com/docs/ios/api/Classes/PFQuery.html
接下来我们将创建一个查询,以查找所有”Name”等于”John”且分数大于1000的对象。首先让我们注释掉之前在didFinishLaunchingWithOp
PFQuery *query = [PFQueryqueryWithClassNam
if(!error){
NSLog(@"Successfully retrieved: %@",objects);
}else{
NSString *errorString = [[error userInfo]objectForKey:@"error"];
NSLog(@"Error:%@",errorString);
按照注释编号来依次来解释下
1.
2.
3.
4.
编译运行该应用!同样此时的操作是异步的,并不会影响UI界面元素快速出现在界面中。接下来会在控制台中看到所输出的查询信息。
好了,我们已经基本了解了如何进行简单的数据存储和查询,接下来就要在真正的项目中进行操作了。
注释掉刚才的数据存储和查询代码,然后准备进行接下来的工作。
用户注册
对这款应用来说,用户要做的第一件事情就是注册账号。
在Xcode中打开RegisterViewController.m,在文件顶部添加一行代码:
#import
该视图目前除了打开和关闭,并没有做任何事情。我们的工作就是当用户触碰Sign Up按钮时完成用户注册工作。
首先查看以下方法的代码:
Sign Up Button pressed
-(IBAction)signUpUserPressed:(id)sender
{
//TODO
//If signup sucessful:
}
这里我们需要添加代码来注册用户,并检查注册是否成功。
使用以下代码替代刚才的方法:
Sign Up Button pressed
-(IBAction)signUpUserPressed:(id)sender
{
//1
PFUser *user =[PFUseruser];
//2
user.username = self.userRegisterTextField.text;
user.password = self.passwordRegisterTextFiel
//3
if(!error){
//The registration was successful, go to the wall
}else{
//something bad has occured
NSString *errorString = [[error userInfo]objectForKey:@"error"];
UIAlertView *errorAlertView = [[UIAlertViewalloc]initWithTitle:@"Error"message:errorStringdelegate:nilcancelButtonTitle:@"Ok"otherButtonTitles:nil, nil];
}
根据注释编号来看看以上代码的作用:
1.
2.
3.
现在来编译运行项目,在登陆界面触碰Sign Up按钮切换到下面的注册界面。
现在来输入新用户名和密码,并触碰Sign Up按钮。如果一切正常,应用就会自动切换到照片墙视图。
太好了!原来一个用户注册系统就这么轻松搞定了!不过我们还得在后台检查一下刚才的操作是否带来了正确结果。在浏览器中切换到Parse的Data Browser页面(如果已经在该页面就刷新一下),就会看到刚才所创建的新用户了。
用户创建成功了!你已经迈出了坚实的一步,接下来就让汹涌而至的用户挤爆服务器吧!
芝麻开门--用户登陆
在这部分我们将学习如何使用刚才所创建的用户来登陆。
切换到LoginViewController.m,在文件顶部添加一行代码:
#import
查看以下方法的代码:
#pragma mark IB Actions
//Login button pressed
-(IBAction)logInPressed:(id)sender
{
}
可以看到,这一部分的代码和注册流程非常象,在触碰登陆按钮时会跳转到照片墙界面。我们将在此使用PFUser类,不过这一次会用于登陆。使用以下代码替代刚才的方法:
#pragma mark IB Actions
//Login button pressed
-(IBAction)logInPressed:(id)sender
{
if(user){
//open the wall
}else{
//something wrong
NSString *errorString = [[error userInfo]objectForKey:@"error"];
UIAlertView *errorAlertView = [[UIAlertViewalloc]initWithTitle:@"Error"message:errorStringdelegate:nilcancelButtonTitle:@"Ok"otherButtonTitles:nil, nil];
}
好吧,这个过程非常简单,和刚才的注册流程几乎没什么大的区别。
编译运行项目,启动应用后,首先会看到登陆界面:
然后用刚才所创建的用户名来登陆。如果登陆成功,会自动切换到照片墙。当然,如果你好奇故意输错信息,也会给出相应的错误提示。
画龙点睛
之前的两个操作(注册和登陆)都会让应用切换到照片墙视图。在该视图中,我们将看到所有用户已上传的照片和评论。
不过,在看到这些东西前,还得先上传一些内容。
切换到UploadImageViewControlle
已登陆的用户可以在照片墙视图触碰Upload按钮,也即rightBarButtonItem。
当用户触碰Upload按钮时,会触发IBActionselectPicturePre
-(IBAction)selectPicturePressed:(id)sender
{
//Open a UIImagePickerController to select the picture
UIImagePickerController *imgPicker = [[UIImagePickerControllera
imgPicker.delegate = self;
imgPicker.sourceType = UIImagePickerControllerS
}
当图片选择完成后,image picker会关闭,然后在主界面的UIImageView中会呈现刚才的图片。
#pragma mark UIImagePicker delegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)imgeditingInfo:(NSDictionary *)editInfo
{
//Place the image in the imageview
self.imgToUpload.image = img;
}
然后用户可以在UITextfieldcommentTextFi
不过这些代码已经有了!我们现在要做的就是完成sendPressed方法。通过该方法,我们将把所选择的图片发送到服务器。
先看看当前的sendPressed方法中的代码:
-(IBAction)sendPressed:(id)sender
{
//Disable the send button until we are ready
self.navigationItem.rightBarButtonItem.enabled = NO;
//Place the loading spinner
UIActivityIndicatorView *loadingSpinner = [[UIActivityIndicatorViewa
//TODO: Upload a new picture
NSData *pictureData = UIImagePNGRepresentation
}
我们将通过两个步骤把图片上传到服务器。首先将图片保存到PFFile对象,然后将其绑定在一个PFObject上,并发送到服务器。
首先别忘了在文件顶部添加以下代码:
#import
然后在sendPressed:方法的最后(NSData那一行的下面)添加代码,修改后的方法如下:
-(IBAction)sendPressed:(id)sender
{
//Disable the send button until we are ready
self.navigationItem.rightBarButtonItem.enabled = NO;
//Place the loading spinner
UIActivityIndicatorView *loadingSpinner = [[UIActivityIndicatorViewa
//TODO: Upload a new picture
NSData *pictureData = UIImagePNGRepresentation
//upload a new picture
//1
PFFile *file = [PFFilefileWithName:@"img"data:pictureData];
if(succeeded){
//2
//add the image to the object,and add the comment and the user
PFObject *imageObject = [PFObjectobjectWithClassN
//3
//4
if(succeeded){
//go back to the wall
else{
NSString *errorString = [[error userInfo]objectForKey:@"error"];
UIAlertView *errorAlertView = [[UIAlertViewalloc]initWithTitle:@"Error"message:errorStringdelegate:nilcancelButtonTitle:@"Ok"otherButtonTitles:nil, nil];
else{
//5
NSString *errorString = [[error userInfo]objectForKey:@"error"];
UIAlertView *errorAlertView = [[UIAlertViewalloc]initWithTitle:@"Error"message:errorStringdelegate:nilcancelButtonTitle:@"OK"otherButtonTitles:nil, nil];
}progressBlock:^(intpercentDone){
NSLog(@"UPloaded: %d %%",percentDone);
}
还是根据注释的编号来解释下:
1.
2.
3.
4.
5.
编译运行该应用!使用之前所注册的用户登陆,然后跳转到上传界面:
触碰Select Picture从照片库中选择一张照片。选择完成后可以写一条评论,然后触碰Send按钮。
此时你可以在控制台中查看上传的百分比信息,当然在实际的应用里面通常用一个进度条来显示。
接下来在浏览器中切换到Data Browser选项卡,然后查看表中名为”WallImageObject”的对象。不过等等,现在我们还没办法在应用中直接看到它。
我们还差最后一步!
在照片墙上显示已上传的照片
WallPicturesViewControll
首先要在WallPicturesViewControll
#import
getWallImages方法用于在视图中加载所有的对象,目前其中的内容如下:
//Get the list of images
-(void)getWallImages
{
//TODO: Get the wall objects from the server
}
使用以下代码替代其中的内容:
//Get the list of images
-(void)getWallImages
{
//Prepare the query to get all the images in descending order
//1
PFQuery *query = [PFQueryqueryWithClassNam
//2
//3
if(!error){
//Everything was correct,put the new objects and load the wall
self.wallObjectsArray = nil;
self.wallObjectsArray = [[NSArrayalloc]initWithArray:objects];
}else{
//4
NSString *errorString = [[error userInfo]objectForKey:@"error"];
UIAlertView *errorAlertView = [[UIAlertViewalloc]initWithTitle:@"Error"message:errorStringdelegate:nilcancelButtonTitle:@"Ok"otherButtonTitles:nil, nil];
}
来解释一下:
1.
2.
3.
4.
一旦对象下载成功后,就可以通过loadWallImages方法在视图中显示。使用以下代码替代loadWallImages方法的内容:
#pragma mark Wall Load
//Load the images on the wall
-(void)loadWallViews
{
//Clean the scroll view
for (idviewToRemovein [self.wallScrollsubviews]){
if([viewToRemoveisMemberOfCl
//For every wall element,put a view in the scroll
intoriginY = 10;
for(PFObject *wallObjectinself.wallObjectsArray){
//1
//Build the view with the image and the comments
UIView *wallImageView = [[UIViewalloc]initWithFrame:CGRectMake(10, originY, self.view.frame.size.width-20, 300)];
//2
//add the image
PFFile *image = (PFFile*)[wallObjectobjectForKey:@"image"];
UIImageView *userImage = [[UIImageViewalloc]initWithImage:[UIImageimageWithData:image.getData]];
userImage.frame = CGRectMake(0, 0, wallImageView.frame.size.width, 200);
//3
//add the info label(user and creation date
NSDate *creationDate = wallObject.createdAt;
NSDateFormatter *df = [[NSDateFormatteralloc]init];
//4
UILabel *infoLabel = [[UILabelalloc]initWithFrame:CGRectMake(0, 210, wallImageView.frame.size.width, 15)];
infoLabel.text = [NSStringstringWithFormat
infoLabel.font = [UIFontfontWithName:@"Arial-ItalicMT"size:9];
infoLabel.textColor = [UIColorwhiteColor];
infoLabel.backgroundColor = [UIColorclearColor];
//5
//add the commment
UILabel *commentLabel = [[UILabelalloc]initWithFrame:CGRectMake(0, 240, wallImageView.frame.size.width, 15)];
commentLabel.text = [wallObjectobjectForKey:@"comment"];
commentLabel.font = [UIFontfontWithName:@"ArialMT"size:13];
commentLabel.textColor = [UIColorwhiteColor];
commentLabel.backgroundColor = [UIColorclearColor];
//6
originY = originY + wallImageView.frame.size.width +20;
//7
//set the bounds of the scroll
self.wallScroll.contentSize = CGSizeMake(self.wallScroll.frame.size.width, originY);
}
来解释下以上的代码:
首先我们清除了scrollView中之前的内容。然后我们会遍历数组中的所有对象,每个怨毒都对应一个PFObject,而该对象拥有一个PFFile的值。
对数组中的每一个对象执行以下操作(按注释编号):
1.
2.
3.
4.
5.
6.
最后,当每一个对象都被解析后,根据上一个视图的大小和位置来决定下一个滚动视图的边界。
编译运行项目,此时你将可以看到之前上传成功的图片。好好试一下上传更多的图片和评论,然后在照片墙中展示~
感觉很爽,不是吗?!
最后的最后-注销
最后一步当然是支持用户注销。这个就更简单了。还是在WallPicturesViewControll
-(IBAction)logoutPressed:(id)sender
{
}
编译运行,一个简单的照片分享应用从技术的角度已经完成了!
这里是完整的项目链接:
https://github.com/toniomg/TutorialParseBaaS
希望你已经了解了Parse中PFObject和PFUser的强大作用,接下来就可以在你自己的应用或游戏中尝试使用parse的后台服务了。