本文由CocoaChina翻译组成员leon(社区ID)翻译自raywenderlich
原文:Parse Tutorial: Getting Started with Web Backends
首先—创建你的后台服务
在开始编写你的app前,你首先要做的是创建你的Parse后台,每个开发人员和每个app都需要一个不同的标识,否则你的数据和账号会和别人的混淆,虽然这会带来一些有趣的副作用,但是你应该使你的数据和别人的区别开来。
第一步就是访问Parse.com,然后在右上角点击Sign Up创建一个新账号。
账号创建好后,你会被要求创建你的第一个app,每个你想要使用后台服务的应用都要单独地注册。在这里,我们把这个应用命名为“tutorialApp”;在Parse中可能存在着很多同名的app,但是你注册的只有一个。
你的app创建好后,来到Dashboard页面,在这里你可以查看你app的数据,这里有一系列的操作按钮,有点像UISegmentedControl,如以下截图:
这里是对屏幕上方一些操作按钮的说明:
- Overview:在这里你可以看到一些关于你app统计信息,如流量,推送信息,调用API的次数等。
- Data Browser:这里你可以看到所有放到你后台里的数据。你也可以看到账号,你可以手动地操作数据,这里就像一个数据库编辑器。
- Push Notifications:可以在这里向你的用户发送推送信息,或者向一个特定组发送推送信息。
- Setting:你可以在这里设置你的app,管理它的安全性,和导出你的数据。
Parse示例项目
为了在这篇教程中创建一个后台服务,这里提供了一个简单的项目来使大家容易入手。你可以下载它并跟着本教程添加Parse服务。示例项目就放在github上。
你可以在https://github.com/toniomg/TutorialBase上下载这个项目,也可以使用以下终端命令:
- git clone https://github.com/toniomg/TutorialBase
在Xcode中打开这个项目,编译运行!首先你会看到一个登陆界面,但目前为止,这个项目还没有加入后台服务,你很快就会完成这些功能。
在继续学习之前,先打开MainStoryboard.storyboard来看一下程序的结构和流程。
这个项目分为4个主要的视图,每个视图在storyboard中都有自己的视图控制器和视图
- Log In:登陆页面有用户名和密码文本框,还有一个Sign Up按钮,在你想注册一个新账号的时候用来前往Sign Up页面。
- Sign Up:在这个视图,用户输入用户名和密码,用来在后台服务中创建一个新账号。
- Wall:这是这个app中的主要界面,这里用户可以看到其他用户上传的带有评论的图片。
- Upload:在这里,用户可以上传带有评论的图片到照片墙上。
注意segue线表示了这个app的流向,包括绕过了Sign Up界面的流向。如下图所示:
准备Parse服务
第一步自然是要配置你的项目使它可以加入Parse服务!
在这个地址下载Parse框架:https://parse.com/downloads/ios/parse-library/latest
下载完框架后,解压并把Parse.framework文件夹拖入你的Xcode项目的Framework文件夹内。记得勾选“Copy item...”和“Create groups...”。
默认下Parse框架会加入到“tutorialBase”中,这也是我们期望的,然后添加其他的框架,完成后的框架列表如下:
(怎么添加框架的步骤就不说了,网上一大把)
下一步就是在app启动时注册后台服务,在AppDelegate.m文件中加入头文件:
然后,在函数didFinishLaunchingWithOptions的开头,加入语句:
- [Parse setApplicationId:AppID clientKey:clientKey];
你会看到有错误出现了,Application ID和Client Key需要一个常量,但是它们现在还是空的——是时候改正了!
为了找到需要的API keys,要去到Parse Dashboard(1),选择你的app(2),然后找到左侧栏,复制Application ID和Client Keys(3),如下图所示:
(注意,我看到的界面与上图有的不同,我是在Settings——Application keys界面中找到所需的App ID和Client Key的)
你可以直接把keys复制到setApplicationId方法中,它们只需使用一次。完成后,这个方法看起来和下面代码是相似的,只是keys有所不同:
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
-
- [Parse setApplicationId:@"UXuHVmNRX44rcczbv1NIIHHbazteYfQU4GAJ8EOS"
- clientKey:@"cqFwq5Vpb19VKPKSe1dOZJrjsQbytPzKa2bEdakx"];
-
- return YES;
- }
编译运行你的app!确定没有任何错误。一切顺利的话,意味着你的app已经绑定Parse后台服务了。你即将开始使用后台服务!
下一步就是创建一些示例对象!
创建示例对象
现在你的项目已经配置好了并连接上Parse,现在花一点时间来复习一下发送对象到后台和从后台获取对象的概念。
你可以根据之前的步骤创建一个新的项目,或者在示例项目中交替地使用AppDelegate文件。
在这个例子中,你会使用PFObject类,这是一个基本的类,提供一些基本的对象操作方法。对于PFObject类的详细详细,你可以参照这个文档:https://parse.com/docs/ios/api/Classes/PFObject.html。
在示例中,你会上传一个叫“Player”的对象,这个对象有“Name”和“Score”字段。在你的数据库中,你会有一个名为“Player”的表,和你上传的所有对象,下面来看看例子:
找到didFinishLaunchingWithOptions方法,之后添加以下代码,连接到Parse后台服务:
- PFObject *player = [PFObject objectWithClassName:@"Player"];
- [player setObject:@"John" forKey:@"Name"];
- [player setObject:[NSNumber numberWithInt:1230] forKey:@"Score"];
- [player save];
注释1:在这一行你创建了一个类名为“Player”的对象。
注释2:在这里你对字段赋上值,name字段的值为“John”,score字段的值为“1230”。
注释3:在这里你保存了对象,对象会同步发送到Parse服务器。
这就可以了!最厉害的是你无须在浏览器上的Parse界面去创建一个表,这个表将根据你上传的对象自动创建。
编译运行你的程序!如果你正确地在代码中设置了keys,还有你的app在上传对象前正确注册到了Parse服务器,那么一切都会很顺利。
但上传的对象在哪?
为了检查你的对象是否正确保存了,在浏览器中打开Parse dashboard界面,点击“Data Browser”,在这里你应该可以看到你上传的对象,如下图:
这就是保存对象最简单的方法。恭喜你成功地和后台进行了交互!
异步操作
你可能已经注意到了,在控制带出现了警告信息,你的app会被阻塞直到你的对象完成上传,这是同步的网络操作!这样你不但不能检查调用的结果,你的用户也会被卡在调用API的等待上。
你的应用可能会因此获得一星评分!当然,还是有解决的办法的。
注释掉之前在didFinishLaunchingWithOptions方法添加的代码,否则你的app在每次运行时都会上传一个新的对象。再添加以下的代码:
- PFObject *anotherPlayer = [PFObject objectWithClassName:@"Player"];
- [anotherPlayer setObject:@"Jack" forKey:@"Name"];
- [anotherPlayer setObject:[NSNumber numberWithInt:840] forKey:@"Score"];
- [anotherPlayer saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
-
- if (succeeded){
- NSLog(@"Object Uploaded!");
- }
- else{
- NSString *errorString = [[error userInfo] objectForKey:@"error"];
- NSLog(@"Error: %@", errorString);
- }
-
- }];
正如你所看到,你使用了异步的方式上传了对象,还有在一个代码块中检查返回结果。随着代码块在iOS中越来越多的使用,你应该对代码块感到熟悉。每个简单的UIView动画目前都是在代码块中完成的。幸运的是,这里有一个教程让你更加熟悉代码块这种特性,链接为 How To Use Block in iOS 5 Tutorial - Part 1
(英文教程)。
编译运行你的app吧!
在Parse Dashboard中检查你是否正确上传了对象到服务器。不同的是这次你的app在上传对象时不会阻塞了。
你应该会注意到你的设备(或模拟器)会出现一个网络活动指示器,指示器旋转时登陆界面会弹出来。过了一会儿,当交互完成后,你会看到控制台中出现NSLog消息。当要上传想image那种要花更长时间去传输的对象时,这是十分有用的。
像之前那样,去到Data Browser界面你就会看到,在通过同步上传的对象的旁边出现了一个通过异步上传的对象。
取得对象
现在,是时候去获取对象了。为了实现这个目的,Parse有一个类PFQuery——它执行查询操作,具体可看PFQuery documentation。
你将会编写代码,去查询Score超过1000,Name为“John”的对象。在这之前,注释掉之前的代码,否则每次运行程序都会上传新的对象。加入以下代码:
- PFQuery *query = [PFQuery queryWithClassName:@"Player"];
- [query whereKey:@"Name" equalTo:@"John"];
- [query whereKey:@"Score" greaterThan:[NSNumber numberWithInt:1000]];
- [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
- if (!error) {
- NSLog(@"Successfully retrieved: %@", objects);
- } else {
- NSString *errorString = [[error userInfo] objectForKey:@"error"];
- NSLog(@"Error: %@", errorString);
- }
- }];
注释1:你创建了一个查询对象,“Player”是你想进行查询的表。
注释2:你只想获取name为“John”的对象。。。
注释3:。。。还有score要超过1000的
注释4:发送查询,在代码块中打印结果。
编译运行你的app!由于操作是异步的,你的UI界面不会卡住——这是让你的用户感到高兴的关键。在控制台中,你会看到符合查询条件的所有对象,如下图所示:
在对基本的存储和查询操作有了简单探索后,你可以继续在项目中操作了。
回到项目中,注释掉刚才的代码。
用户注册
你的用户使用你的app时的第一步就是在注册账号。
在项目中打开RegisterViewController.m文件,添加以下Parse头文件:
正如你所看到的,现在注册视图除了打开和关闭外,还没有添加任何功能。你的任务就是在用户点击“Sign Up”按钮时,可以进行注册操作。
为了实现这个目的,你可以找到关联了这个按钮的IBAction:
-
- -(IBAction)signUpUserPressed:(id)sender
- {
-
-
-
- }
你需要在这里添加注册操作的代码,然后检查是否可以成功注册。
用以下的代码替代上面signUpUserPressed方法中的内容:
-
- -(IBAction)signUpUserPressed:(id)sender
- {
-
- PFUser *user = [PFUser user];
-
- user.username = self.userRegisterTextField.text;
- user.password = self.passwordRegisterTextField.text;
-
- [user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
- if (!error) {
-
- [self performSegueWithIdentifier:@"SignupSuccesful" sender:self];
-
- } else {
-
- NSString *errorString = [[error userInfo] objectForKey:@"error"];
- UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:@"Error" message:errorString delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
- [errorAlertView show];
- }
- }];
- }
在以上代码中,创建一个用户的步骤是:
注释1:创建一个新的PFUser对象,这个类是用来登陆和注册的。它会保储已经通过验证的用户,你可以在你想要的时候访问这个用户的数据。你可以在这里找到关 于PFUser的文档。
注释2:指定TextFields中的内容为用户名和密码。
注释3:调用在后台注册用户的方法,在代码块中检查结果。结果有两种可能,一种是注册成功,新用户被创建,然后视图转到照片墙视图。或者是注册失败,产生错误信息,你可以对用户显示错误描述。
编译运行你的app看是否有错误!
为了检查用户注册操作,运行程序,在Log In界面按下Sign Up按钮,你会看到下图:
输入用户名和密码,按下Sign Up按钮,如果顺利的话,app会转到照片墙视图。
好极了!但你仍需要确认你的新用户是否已经保存了。打开Data Browser来确认新用户是否存在,如下图所示:
恭喜!你第一个用户已经创建了!现在是时候让用户登陆并和后台交互了!
用户登陆
在这一节,你会学习怎样让你在上面创建的用户进行登陆。
打开LoginviewController.m文件,加上头文件:
然后看到以下代码:
-
- -(IBAction)logInPressed:(id)sender
- {
-
-
-
- }
正如你所看到的,这一部分和注册部分的代码很相似!你会再次使用PFUser类,但这次你是用它来进行登陆操作的。用以下的代码替换掉loginPressed方法中的内容:
-
- -(IBAction)logInPressed:(id)sender
- {
- [PFUser logInWithUsernameInBackground:self.userTextField.text password:self.passwordTextField.text block:^(PFUser *user, NSError *error) {
- if (user) {
-
- [self performSegueWithIdentifier:@"LoginSuccesful" sender:self];
- } else {
-
- NSString *errorString = [[error userInfo] objectForKey:@"error"];
- UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:@"Error" message:errorString delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
- [errorAlertView show];
- }
- }];
- }
代码和注册代码很相似。运行app,你会看到以下界面:
尝试用你刚才创建的用户进行登陆,如果顺利的话,app应该会转到照片墙视图。
照片墙
刚才的操作(注册和登陆)都会使视图转到照片墙视图,在这个视图中,你可以看到所用的注册用户上传到后台服务器的带有评论的照片。
但这在看到照片之前,也得先有照片上传上去啊!
在Parse上传文件非常简单,先看一看UploadImageViewController.m文件,文件就是在这里进行上传的。
一个已经登陆的用户可以点击在照片墙视图的Upload按钮,就是导航栏右边的按钮,上传文件的过程就在下面给出。
用户点击Upload按钮时,就会触发selectPicturePressed方法。系统的照片库就会出现,让用户从中选择一张图片,如下所示:
- -(IBAction)selectPicturePressed:(id)sender
- {
-
- UIImagePickerController *imgPicker = [[UIImagePickerController alloc] init];
- imgPicker.delegate = self;
- imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
-
- [self.navigationController presentModalViewController:imgPicker animated:YES];
- }
选好图片后,图片就会出现在主界面的UIImageView中,如下所示:
- - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo
- {
- [picker dismissModalViewControllerAnimated:YES];
-
- self.imgToUpload.image = img;
- }
用户可以在文本框中输入对照片的评论,也可以不输评论,这是可选的。
这些都已经为你做好了!现在应该添加代码来实现sendPressed方法了。这个方法是导航栏上的按钮触发的,用来上传图片和评论到服务器。
过程分开两个部分。首先,用PFFile对象上传图片;然后把对象附到PFObject上传到服务器。
先在UploadImageViewController.m文件开头加上头文件:
在-(IBAction)sendPressed:(id)sender方法中加入以下代码:
-
-
- PFFile *file = [PFFile fileWithName:@"img" data:pictureData];
- [file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
-
- if (succeeded){
-
-
- PFObject *imageObject = [PFObject objectWithClassName:@"WallImageObject"];
- [imageObject setObject:file forKey:@"image"];
- [imageObject setObject:[PFUser currentUser].username forKey:@"user"];
- [imageObject setObject:self.commentTextField.text forKey:@"comment"];
-
- [imageObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
-
- if (succeeded){
-
- [self.navigationController popViewControllerAnimated:YES];
- }
- else{
- NSString *errorString = [[error userInfo] objectForKey:@"error"];
- UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:@"Error" message:errorString delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
- [errorAlertView show];
- }
- }];
- }
- else{
-
- NSString *errorString = [[error userInfo] objectForKey:@"error"];
- UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:@"Error" message:errorString delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
- [errorAlertView show];
- }
- } progressBlock:^(int percentDone) {
- NSLog(@"Uploaded: %d %%", percentDone);
- }];
下面来解析一下以上代码:
注释1:用图片创建PFFile对象,然后在后台保存它。在代码块的末尾你可以检查上传的进度。
注释2:上传成功后,创建含有图片和数据的PFObject对象,对象中设置文件,用户名和评论。
注释3:在后台中保存PFObject对象。
注释4:如果成功了,就返回到照片墙视图。
注释5:失败了就通知用户。
编译运行你的app!用之前创建的用户名登陆,来到Upload界面,如下图所示:
按下Select Picture在相册中选择一张图片,如果想的话,可以写上评论,在按下Send按钮。
你可以在控制台中看到上传的进度。
现在去到Data Browser界面看看,你会看到一个新的名为“WallImageObject”的表和里面的对象,但等等,在app中还没看到这些图片对象呢!!
现在是时候获取这些图片了!
在照片墙上贴上图片
WallPicturesViewController.m文件用于展示用户已上传的照片。
在WallPictureController.m文件开头加上头文件:
getWallImage方法会在视图加载获取图片时调用,现在这个方法还是空的,在这个方法中加入以下代码:
- -(void)getWallImages
- {
-
-
- PFQuery *query = [PFQuery queryWithClassName:@"WallImageObject"];
-
- [query orderByDescending:@"createdAt"];
- [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
-
- if (!error) {
-
- self.wallObjectsArray = nil;
- self.wallObjectsArray = [[NSArray alloc] initWithArray:objects];
- [self loadWallViews];
-
- } else {
-
-
- NSString *errorString = [[error userInfo] objectForKey:@"error"];
- UIAlertView *errorAlertView = [[UIAlertView alloc] initWithTitle:@"Error" message:errorString delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
- [errorAlertView show];
- }
- }];
-
- }
解释一下:
注释1:用你想查询的表的名字创建一个查询对象。
注释2:查询结果根据“createdAt”属性降序排列。
注释3:找到符合条件的对象,把WallImageObject类型的对象展示出来。如果顺利的话,把下载到的对象替代wallObjectArray数组中的内容。
注释4:出错了的话就告知用户。
对象下载好后,就要在照片墙上展示它了!看到loadWallImage方法,加入以下代码:
- -(void)loadWallViews
- {
-
- for (id viewToRemove in [self.wallScroll subviews]){
-
- if ([viewToRemove isMemberOfClass:[UIView class]])
- [viewToRemove removeFromSuperview];
- }
-
-
- int originY = 10;
-
- for (PFObject *wallObject in self.wallObjectsArray){
-
-
-
- UIView *wallImageView = [[UIView alloc] initWithFrame:CGRectMake(10, originY, self.view.frame.size.width - 20 , 300)];
-
-
-
- PFFile *image = (PFFile *)[wallObject objectForKey:@"image"];
- UIImageView *userImage = [[UIImageView alloc] initWithImage:[UIImage imageWithData:image.getData]];
- userImage.frame = CGRectMake(0, 0, wallImageView.frame.size.width, 200);
- [wallImageView addSubview:userImage];
-
-
-
- NSDate *creationDate = wallObject.createdAt;
- NSDateFormatter *df = [[NSDateFormatter alloc] init];
- [df setDateFormat:@"HH:mm dd/MM yyyy"];
-
- UILabel *infoLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 210, wallImageView.frame.size.width,15)];
- infoLabel.text = [NSString stringWithFormat:@"Uploaded by: %@, %@", [wallObject objectForKey:@"user"], [df stringFromDate:creationDate]];
- infoLabel.font = [UIFont fontWithName:@"Arial-ItalicMT" size:9];
- infoLabel.textColor = [UIColor whiteColor];
- infoLabel.backgroundColor = [UIColor clearColor];
- [wallImageView addSubview:infoLabel];
-
-
-
- UILabel *commentLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 240, wallImageView.frame.size.width, 15)];
- commentLabel.text = [wallObject objectForKey:@"comment"];
- commentLabel.font = [UIFont fontWithName:@"ArialMT" size:13];
- commentLabel.textColor = [UIColor whiteColor];
- commentLabel.backgroundColor = [UIColor clearColor];
- [wallImageView addSubview:commentLabel];
-
-
- [self.wallScroll addSubview:wallImageView];
-
-
- originY = originY + wallImageView.frame.size.width + 20;
-
- }
-
-
-
- self.wallScroll.contentSize = CGSizeMake(self.wallScroll.frame.size.width, originY);
-
- }
第一步就是清理scrollView中的东西,然后你遍历了数组中的内容。每个数组元素就是一个PFObject对象,对象包含了一个PFFile。
对于数组中的每个对象:
注释1:创建包含图片和评论的视图。
注释2:取得图片对象数据(从PFFile中),然后用数据初始化一个UIImageView对象。
注释3:取得对象的创建信息。
注释4:取得上传图片的用户名,把它和创建数据放到一个label中。
注释5:将评论放到另一个label中。
注释6:将内容添加到scrollView中。
当每个对象都被解析过后,就根据最后的视图的大小和位置来设置scrollview的边界。
编译运行app!不出意外的话,你就会看到你之前上传的图片和评论了。花点时间上传更多的图片和评论,看看它们在照片墙上展示出来的效果吧。
感觉很好吧?
退出登陆
这篇教程的最后一部分就是怎么让用户退出登陆。为此,看到WallPictureViewController.m文件中的logoutPressed方法,在这个方法中加入以下代码:
- -(IBAction)logoutPressed:(id)sender
- {
- [PFUser logOut];
- [self.navigationController popViewControllerAnimated:YES];
- }
运行项目,这就搞定了!