Parse-IOS-SDK-应用开发-全-
Parse IOS SDK 应用开发(全)
原文:
zh.annas-archive.org/md5/667af100d77d4d56b746002603b5f905译者:飞龙
前言
Parse 是提供强大且可靠的桌面和移动平台(如 iOS、Android、Windows Phone 8、JavaScript、OS X 和 Windows 8)后端服务的首选解决方案。Parse 为您提供所有平台的 SDK,这有助于您迅速提供强大且可靠的后端服务。
Parse SDK 为您提供了一种独立创建应用程序的方法,无需依赖任何服务器端代码和大量的网络服务代码。Parse 强调快速应用开发。它显著加速了开发过程。Parse 平台易于使用、可扩展且可靠。
本书将教授您如何使用 Parse SDK 开始应用开发的基础知识。由于本书旨在快速让您熟悉 Parse SDK 的所有重要方面,因此它将非常快速地涵盖许多主题。在整个书中,我们将通过有效的示例和代码直接实现来处理。这种方法将帮助您快速运行示例代码并将其集成到您的项目中。
使用 Parse SDK 进行操作非常简单;这将使您能够迅速创建您的应用程序。本书使用代码和许多有用的提示来解释所有主题。
其中一个最重要的关键点是云代码,您可以将所有需要进行大量操作的自定义代码放在这里。这对于多平台应用开发也非常有用,它允许整个平台使用同一套代码。Parse 云服务还允许您托管您的网站,并且 Parse 还为您提供预构建的电子邮件验证和重置密码邮件。使用这些服务,您可以向用户发送验证邮件以验证他们的电子邮件地址,并分别重置他们的密码。
本书涵盖内容
本书旨在让您熟悉使用 Parse SDK 进行应用开发的基本原理。为了实现这一目标,本书以易于理解的方式组织了示例和代码。
第一章,开始使用 Parse,首先探讨使用 Parse 的优势。然后,我们将学习如何在 Parse 上创建应用程序及其集成到您的项目中。最后,我们将通过向 Parse 云中添加一些示例数据来结束本章。
第二章,Parse 对象和查询,解释了 Parse 对象及其操作。我们将学习关于关系和数据类型。我们还将学习如何处理查询,包括复杂和关系查询。
第三章,子类和文件,将解释子类化 PFObject 的方法及其代码优化定制。本章还解释了处理您的文件和数据的方法。
第四章,Parse 分析,首先探讨将 Parse 分析集成到你的项目中的方法以及读取分析数据。最后,我们将了解数据使用及其在市场分析中的优势。
第五章,推送通知,首先解释在 Apple 开发者门户上推送通知的配置。然后,我们将学习在我们的项目中安装推送通知,接着发送通知。然后,我们将学习推送通知的发送选项。最后,我们将学习对收到的通知的负载(或数据)进行响应。
第六章,用户和角色,解释了用户的概念,PFUser,以及你可以在应用程序中轻松集成登录和注册工作流程的各种方法。我们还将学习用户数据管理和安全。此外,我们将学习角色以保护应用程序数据,并限制其仅对一组用户的使用。我们还将查看角色层次结构,并对其进行详细探讨。
第七章,社交媒体集成,讨论了将社交媒体集成到你的应用程序中的方法。我们将探讨将 Facebook 集成并链接到现有用户的方法。接下来,我们将查看与 Parse 的 Twitter 集成,并探讨一些我们可以将其集成到注册工作流程中的方法。
第八章,使用云函数,首先探讨将我们的代码集成和托管在云上的方法。然后,我们将使用移动应用程序上的代码以最小化客户端的进程负载。然后,我们将查看 Parse 上的托管站点。然后,我们将深入研究并学习一些在 Parse 云上的复杂函数实现。
第九章,错误处理和安全,将解释可能发生的各种错误类型及其处理方法。然后我们将探讨在 Parse 上保护数据的方法以及如何从 Parse 导出我们的数据。
你需要这本书的内容
你需要以下这些东西才能开始使用 Parse SDK 为 iOS 设备编写应用程序:
-
运行 Leopard(OS X 10.5.3 或更高版本)的基于 Intel 的 Macintosh
-
Xcode
-
iOS SDK 开发工具包
-
你必须注册为 iPhone 开发者,才能在你的设备上测试示例项目
这本书面向的对象
如果你想要立即开发一个应用程序而不依赖于外部网络服务,那么这本书适合你,因为在这本书中,你将学习关于 Parse 的内容,它作为一个后端服务。这是一个基于云的平台,它从你的头脑中提取一切,并提供 SDK 以将其集成到不同的环境中。
本书使用 Objective-C 作为其主要语言,因此您必须具备一些 Objective-C 的基本知识。本书假设您理解面向对象编程的基础,以及一般的编程。
本书旨在让您立即开始使用 Parse SDK,因此您应该熟悉 iPhone/iPad 开发。iPhone 是一个优秀的编程平台。它看起来很漂亮,手感很好。Parse 解决了您的数据存储问题和依赖网络服务的问题,并减少了项目开发成本和时间。
惯例
在本书中,您将找到许多不同信息的文本样式,以区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。
文本中的代码词汇、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称将如下所示:“在application:didFinishLaunchingWithOptions:函数内部添加以下代码:”。
代码块设置如下:
PFObject *demoObject = [PFObject objectWithClassName:@"Demo"]; // 1
[demoObject setObject:@"data value" forKey:@"data column"]; // 2
[demoObject save]; // 3
任何命令行输入或输出将如下所示:
curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash
新术语和重要词汇以粗体显示。你在屏幕上看到的,例如在菜单或对话框中的文字,在文本中会这样显示:“创建应用程序后,你将获得应用程序 ID和客户端密钥:”。
注意
警告或重要注意事项如下所示。
小贴士
小技巧和技巧如下所示。
读者反馈
我们欢迎读者的反馈。请告诉我们您对这本书的看法——您喜欢什么或可能不喜欢什么。读者反馈对我们开发您真正能从中获得最大价值的标题非常重要。
要发送一般反馈,只需将电子邮件发送到<feedback@packtpub.com>,并在邮件主题中提及书名。
如果您在某个主题上具有专业知识,并且您对撰写或为书籍做出贡献感兴趣,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在您是 Packt 书籍的骄傲所有者,我们有一些事情可以帮助您从您的购买中获得最大价值。
下载示例代码
您可以从您在www.packtpub.com的账户下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。
错误更正
尽管我们已经尽最大努力确保内容的准确性,错误仍然可能发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以避免其他读者感到沮丧,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情来报告。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站,或添加到该标题的勘误部分下的现有勘误列表中。您可以通过从www.packtpub.com/support选择您的标题来查看任何现有的勘误。
盗版
互联网上版权材料的盗版是一个持续存在的问题,跨越所有媒体。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现我们作品的任何非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
如果您在本书的任何方面遇到问题,请通过<copyright@packtpub.com>联系我们,并提供涉嫌盗版材料的链接。
我们感谢您在保护我们作者和我们为您提供有价值内容的能力方面的帮助。
问题
如果您在本书的任何方面遇到问题,可以通过<questions@packtpub.com>联系我们,我们将尽力解决。
第一章. 开始使用 Parse
Parse 是一个基于云的应用开发平台,它为你提供了一种可扩展且强大的后端服务。这个平台适用于 Windows、iOS、Android、JavaScript 和 OS X。Parse 为你提供了一套即插即用的服务,包括社交媒体集成、推送通知和具有完全自定义灵活性的数据存储。Parse 基本上强调快速应用开发,使用它你可以减少开发时间和努力。
在本章中,我们将涵盖:
-
使用 Parse 作为我们应用后端的优势
-
在 Parse 网络门户上设置应用
-
将 Parse SDK 集成到我们的项目中
-
开始使用 Parse 的基本代码结构
Parse 后端服务
基于传统的基于数据的应用开发流程涉及后端开发,这增加了应用的开发时间和成本。所有在商店中可用的主要应用都使用了后端 API,这本身就是一个复杂的过程。
开发者在以传统方式开发应用时面临以下问题:
-
开发时间和努力大
-
服务器增加导致整体产品成本上升
-
需要单独的开发和生产服务器进行托管和维护
-
更容易出错
-
当服务器宕机或维护时,应用会崩溃
为了克服传统应用开发的缺点,开发者可以利用 Parse 为他们的应用创建一个强大的后端。Parse 内置了许多在移动应用开发中经常需要的特性。它减少了应用开发所需的努力、成本和时间。
Parse 允许你创建应用,无需担心创建应用的后端。使用 Parse,你可以创建一个灵活且强大的后端,无需担心服务器管理,也不必花费时间编写后端服务器的代码。Parse 提供了一套完整的框架,包括推送通知、社交媒体集成、分析和数据存储,并允许使用 Cloud Code 进行后端业务逻辑的编码。
使用 Parse 的优势
使用 Parse 作为后端服务的优势有很多。以下列出了一些:
-
快速应用开发:Parse 允许你使用原生框架组件快速开发应用,并提供完全灵活的即插即用设施。
-
UX 丰富的应用:Parse 允许开发者更多地专注于创建出色的用户体验,而无需担心服务器维护和复杂的架构。当你将 Parse SDK 集成到你的应用中时,你可以立即添加推送通知、数据存储、社交集成(等等!)。
-
强大的数据管理:Parse 处理您在云上安全高效存储所需的一切。您只需几行代码就可以存储基本数据类型、位置、照片和查询。您可以使用基于网络的浏览器管理、搜索、过滤和更新您的内容。
-
使您的应用程序社交化:通过社交媒体网站(如 Facebook 和 Twitter)使用几行代码连接您的应用程序用户。Parse 负责跨网络链接账户、重置密码,并确保一切安全,这样您就不必担心了。
-
即插即用推送通知:Parse 简化了向应用程序添加实时推送通知的努力。您可以通过基于网络的推送控制台、REST API 或客户端 SDKs 创建、发送和定位高度有效的推送通知。您每天可以发送数百万条通知;您永远不必担心扩展问题。
-
运行自定义应用程序代码:不再需要服务器来通过 Parse 的名为 Cloud Code 的适当命名功能向应用程序的后端添加丰富的自定义业务逻辑。Parse 为您提供 Cloud Code 控制台和 Cloud Modules;它们可以与任何第三方服务(如 Twilio、MailGun、CrowdFlower 以及更多)虚拟集成。
-
一站式后端:Parse 为 iOS、Android、Windows 8、Windows Phone 8、OS X、Unity、Xamarin 和 JavaScript 提供了原生 SDKs;这使得为所有设备和环境创建美观且强大的应用程序变得容易。从桌面到移动应用程序,Parse 为每个人提供了 SDK。
在 Parse 上设置账户和应用程序
以下步骤将帮助您在 Parse.com 上设置账户:
-
要开始使用 Parse 开发应用程序,您需要在 Parse.com 上创建您的应用程序。您可以通过访问
www.parse.com/apps/quickstart来设置您的账户。 -
按照说明并在 Parse.com 上注册。
-
通过提供您希望与 Parse 集成的名称来创建您的应用程序。
![在 Parse 上设置账户和应用程序]()
-
创建应用程序后,您将获得应用程序 ID和客户端密钥:
![在 Parse 上设置账户和应用程序]()
在 iOS 项目中集成 Parse
以下步骤将帮助您将 Parse 集成到您的项目中:
-
下载 Parse iOS SDK。
-
您需要最新的 Xcode(v5.0+)并针对 iOS 4.3 或更高版本。
![在 iOS 项目中集成 Parse]()
-
确保已勾选将项目复制到目标组文件夹复选框:
![在 iOS 项目中集成 Parse]()
-
创建项目后,它应该看起来像以下截图:
![在 iOS 项目中集成 Parse]()
-
将框架添加到项目的编译资源中。点击您应用的名称(在我们的例子中是Quiz)下的目标 | Quiz | 构建阶段选项卡,然后展开链接二进制与库,如图所示:
![在 iOS 项目中集成 Parse]()
-
点击链接二进制与库左下角的加号按钮:
![在 iOS 项目中集成 Parse]()
-
添加以下库:
-
CoreLocation.framework![在 iOS 项目中集成 Parse]()
-
CoreGraphics.framework -
libz.1.1.3.dylib -
MobileCoreServices.framework -
QuartzCore.framework -
Security.framework -
StoreKit.framework -
SystemConfiguration.framework -
AudioToolbox.framework -
CFNetwork.framework
-
设置 Parse
现在我们已经配置了项目,是时候将 Parse 集成到我们的应用程序代码中了。按照以下步骤设置基本代码以开始使用 Parse:
-
打开
AppDelegate.m文件,并在文件顶部添加以下import语句:#import <Parse/Parse.h> -
在
application:didFinishLaunchingWithOptions:函数内添加以下代码:[Parse setApplicationId:@"YOUR APP ID" clientKey:@"YOUR CLIENT KEY"]; -
为了在应用程序打开时跟踪统计信息,添加以下代码行:
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions]; -
编译并运行。
-
如果您针对的是低于 5.0 的 iOS 版本,您需要在目标构建设置中的其他链接器标志条目中添加-fobjc-arc标志:
![设置 Parse]()
就这样!您已经在项目中完成了基本的 Parse 设置。现在您可以准备测试在 Parse 上的第一个应用程序了。
将以下代码片段添加到您的appDelegate.m文件中application:didFinishLaunchingWithOptions:方法的末尾:
PFObject *demoObject = [PFObject objectWithClassName:@"Demo"]; // 1
[demoObject setObject:@"data value" forKey:@"data column"]; // 2
[demoObject save]; // 3
那么,这三行代码究竟在做什么呢?以下是描述:
-
第 1 行:这一行简单地创建了一个名为
"Demo"的 Parse 对象。所以,如果 Parse 上不存在名为"Demo"的类名,它将创建一个新的名为"Demo"的类。 -
第 2 行:这一行将您的数据与类内的一个键关联起来。我们可以将其理解为表格的行-列关系。类名是数据库表名,在我们的例子中是
"Demo",而"data column"ID 是存储"data value"数据的列名。 -
第 3 行:这一行负责在 Parse 上实际保存您的数据。
使用前面的代码行执行您的应用程序。一个名为"Demo"的新对象将被发送到 Parse 云并保存。
您可以立即检查代码的结果:转到基于 Web 的 Parse仪表板,然后点击您的应用程序。点击数据浏览器选项卡,您将在类部分看到一个名为"Demo"的类。当您点击"Demo"类时,您将观察到该行由以下列组成:
-
objectId:这是一个由 Parse 自动生成的字段,包含一个唯一的标识符,它将作为您行的主键。 -
数据列:这个列是由我们的代码生成的;如果您注意的话,您会看到它包含通过代码发送的值("数据值")。 -
createdAt:这也是一个自动生成的字段,它包含行被创建时的日期和时间。 -
updatedAt:这也是一个自动生成的字段,它包含行被更新时的日期和时间。 -
ACL:这个字段用于访问控制列表,用于数据安全。我们将在接下来的章节中详细讨论这个字段。
最后,我们在 Parse 站点上配置了 Parse 应用程序,并配置了项目以从 Parse 发送和接收数据。
摘要
在本章中,我们学习了如何在 Parse 上设置应用程序,包括一些基本的代码和概念,以帮助我们开始使用 Parse。我们首先在 Parse 上创建了一个应用程序。然后,我们将 Parse 集成到您的 iOS 项目中。最后,我们编写了一些代码以开始使用 Parse。
在下一章中,我们将学习关于 Parse 对象和查询的内容。
第二章:Parse 对象和查询
Parse 帮助你管理移动应用程序的完整后端结构,并取消开发复杂服务器端代码及其维护的需求。Parse 以对象的形式提供数据库结构。它以对象的形式保存所有你的数据,并在从 Parse 检索时返回相同的对象。
每个应用程序都有一个与 Client Key 相关的不同且特定的 Application ID,对于同一用户的同一应用程序,该 ID 保持不变。
Parse 基于面向对象的原则。所有对 Parse 的操作都将以对象的形式进行。Parse 以你发送的对象的形式保存你的数据,并帮助你以相同的格式再次获取数据。在本章中,你将了解对象以及可以在 Parse 对象上执行的操作。
在本章中,我们将学习如何与 Parse 对象一起工作,并编写查询以从 Parse 设置和获取数据。
Parse 对象
Parse 中的所有数据都以 PFObject 的形式保存。当你通过执行查询从 Parse 获取任何数据时,结果将以 PFObject 的形式呈现。PFObject 的详细概念将在以下章节中解释。
PFObject
存储在 Parse 上的数据以对象的形式存在,并且围绕 PFObject 开发。PFObject 可以定义为 JSON 数据的关键值(字典格式)对。Parse 数据是无模式的,这意味着你不需要提前指定每个 PFObject 上存在的键。Parse 后端将负责以你想要的任意键值对集合的形式简单地存储你的数据。
假设你正在使用你的应用程序跟踪具有用户 ID 的用户名的访问次数。一个单一的 PFObject 可以包含以下代码:
visitedCount:1122, userName:"Jack Samuel", userId:1232333332
Parse 只接受字符串作为 Key。值可以是字符串、数字、布尔值,甚至是数组、字典——任何可以 JSON 编码的内容。
PFObject 的类名用于区分不同类型的数据。比如说,你调用用户的 visitedCounts 对象。Parse 建议你编写类名 NameYourClassLikeThis 和 nameYourKeysLikeThis,以便提供代码的可读性。正如你在前面的示例中看到的,我们使用了 visitedCounts 来表示访问次数键。
对 Parse 对象的操作
你可以对 Parse 对象执行保存、更新和删除操作。以下是对可以在 Parse 对象上执行的操作的详细说明。
保存对象
要在 Parse 云上保存你的 User 表并添加额外的字段,你需要遵循类似于 NSMutableDictionary 方法的编码约定。更新数据后,你必须调用 saveInBackground 方法将其保存到 Parse 云。以下是一个示例,说明如何在 Parse 云上保存额外的数据:
PFObject *userObject = [PFObject currentUser];
[userObject setObject:[NSNumber numberWithInt:1122] forKey:@"visitedCount"];
[userObject setObject:@"Jack Samuel" forKey:@"userName"];
[userObject setObject:@"1232333332" forKey:@"userId"];
[userObject saveInBackground];
在执行上述代码后,您的数据已保存到 Parse Cloud。您可以在 Parse 应用程序的数据浏览器中检查您的数据。它应该类似于以下代码行:
objectId: "xWMyZ4YEGZ", visitedCount: 1122, userName: "Jack Samuel", userId: "1232333332",
createdAt:"2011-06-10T18:33:42Z", updatedAt:"2011-06-10T18:33:42Z"
这里有两点需要注意:
-
在运行您的代码之前,您不需要配置或设置一个新的名为 User 的类。Parse 将在第一次遇到它时自动创建该类。
-
还有一些字段您不需要指定,这些字段作为便利提供:
-
objectId是每个保存对象的唯一标识符。 -
createdAt和updatedAt表示每个对象在 Parse Cloud 中被创建和最后修改的时间。这些字段中的每一个都是由 Parse 填充的,因此它们在 PFObject 上不存在,直到保存操作完成。
注意
您可以使用 Parse 提供的
saveInBackgroundWithBlock或saveInBackgroundWithTarget:selector:方法在回调操作成功或失败后提供额外的逻辑:[userObject saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { if (succeeded) NSLog(@"Success"); else NSLog(@"Error %@",error); }]; -
小贴士
下载示例代码
您可以从您在www.PacktPub.com的账户下载所有已购买的 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.PacktPub.com/support并注册,以便将文件直接通过电子邮件发送给您。
获取对象
从 Parse Cloud 获取保存的数据甚至比保存数据更容易。您可以通过以下方式从 Parse Cloud 获取数据。
您可以使用 PFQuery 从其objectId获取完整对象。从云中获取数据的方法是异步的。您可以通过使用 Parse 提供的基于块的或基于回调的方法来实现:
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"]; // 1
[query getObjectInBackgroundWithId:@"xWMyZ4YEGZ" block:^(PFObject *gameScore, NSError *error) { //2
// Do something with the returned PFObject in the gameScore variable.
int score = [[gameScore objectForKey:@"score"] intValue];
NSString *playerName = [gameScore objectForKey:@"playerName"]; //3
BOOL cheatMode = [[gameScore objectForKey:@"cheatMode"] boolValue];
NSLog(@"%@", gameScore);
}];
// The InBackground methods are asynchronous, so the code written after this will be executed
// immediately. The codes which are dependent on the query result should be moved
// inside the completion block above.
让我们逐行分析如下:
-
第 1 行:它创建一个指向参数中给出的类名的查询对象。
-
第 2 行:它在第 1 行创建的查询对象上调用异步方法,以获取作为参数提供的
objectId的完整对象。由于我们使用的是基于块的方法,我们可以在块内提供代码,该代码将在成功或失败时执行。 -
第 3 行:它从查询响应中获取的
PFObject读取数据。
Parse 提供了一些所有 Parse 对象的公共值作为属性:
NSString *objectId = gameScore.objectId;
NSDate *updatedAt = gameScore.updatedAt;
NSDate *createdAt = gameScore.createdAt;
要刷新当前 Parse 对象,请输入:
[myObject refresh];
此方法可以在任何 Parse 对象上调用,这在您想要刷新对象数据时很有用。比如说,您想重新验证用户,那么您可以在用户对象上调用refresh方法来刷新它。
离线保存对象
Parse 为你提供了在用户离线时保存数据的函数。因此,当用户未连接到互联网时,数据将保存在本地对象中,一旦用户连接到互联网,数据将自动保存到 Parse 云上。如果你的应用程序在建立连接之前被强制关闭,Parse 将在下次应用程序打开时再次尝试保存对象。对于此类操作,Parse 为你提供了saveEventually方法,这样即使用户未连接到互联网,你也不会丢失任何数据。最终所有调用都是按照请求的顺序执行的。以下代码演示了saveEventually调用:
// Create the object.
PFObject *gameScore = [PFObject objectWithClassName:@"GameScore"];
[gameScore setObject:[NSNumber numberWithInt:1337] forKey:@"score"];
[gameScore setObject:@"Sean Plott" forKey:@"playerName"];
[gameScore setObject:[NSNumber numberWithBool:NO] forKey:@"cheatMode"];
[gameScore saveEventually];
更新对象
对于在 Parse 上更新对象,我们只需要通过调用任何保存函数将新数据提供给 Parse。
例如,假设你有一个保存在 Parse 上的对象的objectId。我们可以使用 Parse 的PFQuery获取相应的PFObject:
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
// Retrieve the object by id
[query getObjectInBackgroundWithId:@"xWMyZ4YEGZ" block:^(PFObject *gameScore, NSError *error) {
// Now let's update it with some new data. In this case, only cheatMode and score
// will get sent to the cloud. playerName hasn't changed.
[gameScore setObject:[NSNumber numberWithBool:YES] forKey:@"cheatMode"];
[gameScore setObject:[NSNumber numberWithInt:1338] forKey:@"score"];
[gameScore saveInBackground];
}];
执行前面的代码后,Parse 客户端将自动检测已更改的字段,并将仅更改的字段发送到 Parse 进行更新。在这里,我们使用saveInBackground方法异步地将我们的数据保存到 Parse 云上。
更新计数器
在某些情况下,你可能需要更新计数器,例如在游戏得分的情况下。然后,在这些情况下,为了增加键的值,你可以使用incrementKey方法,而对于减少值,你可以使用 PFObject 上的decrementKey方法:
[gameScore incrementKey:@"score"];
[gameScore saveInBackground];
我们甚至可以使用以下行代码按任何特定数字增加或减少计数器的值:
[gameScore incrementKey:@"score" byAmount:8];
上一行代码将"score"键增加8。
以数组格式存储数据
要以数组格式存储数据,Parse 提供了几种保存数据的方法。要将数据添加到键的现有值中,你可以使用以下代码片段。此代码将"flying"对象添加到"skills"键中:
[gameScore addObject:@"flying" forKey:@"skills"];
以下行代码将提供的对象数组添加到现有的云数组中。(键的值可以包含重复数据):
[gameScore addObjectsFromArray:[NSArray arrayWithObjects:@"flying", @"kungfu", nil] forKey:@"skills"];
在上一行代码中,数组的数据将被追加到现有的云数据中,而不检查数组值的唯一性。如果你想要避免在云上保存重复的值,你可以使用addUniqueObjectsFromArray方法来保存你的数组。此方法将确保只为键保存唯一的值。以下行代码将在提供的对象数组尚未添加的情况下将其添加到现有数组中:
[gameScore addUniqueObjectsFromArray:[NSArray arrayWithObjects:@"flying", @"kungfu", nil] forKey:@"skills"];
[gameScore saveInBackground];
删除对象
以下行代码将帮助你从 Parse 云中删除对象。此方法将异步删除你的对象:
[myObject deleteInBackground];
你可以使用deleteInBackgroundWithBlock:方法在对象删除后获得回调。
以下代码将删除背景中的对象,并返回响应中的成功或错误消息:
[ myObject deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
// handle error
}
if (succeeded) {
//code on success
}
}];
关系和数据类型
就像我们的数据库中的关系一样,我们有多张相互关联的表。Parse 为我们提供了在 Parse 类之间建立关系的便利。Parse 中的关系将在以下部分中详细解释。
关系
您可以在两个 Parse 对象之间添加关系。为此,我们可以在另一个PFObject中将一个PFObject作为值添加。
让我们举一个例子,我们有两个Course和Student对象,我们可以通过在它们的类之间建立关系来将课程与学生关联起来:
// Create course data
PFObject *myCourse = [PFObject objectWithClassName:@"Course"];
[myCourse setObject:@"MBA" forKey:@"courseName"];
[myCourse setObject:@"2 years" forKey:@"courseDuration"];
// Create student data
PFObject *studentData = [PFObject objectWithClassName:@"Student"];
[studentData setObject:@"Jack Samuel" forKey:@"name"];
[studentData setObject:[NSNumber numberWithInt:22] forKey:@"age"];
// Add a relation between the student and course
[studentData setObject:myCourse forKey:@"registeredCourse"];
// This will save both myPost and myComment
[studentData saveInBackground];
通过使用objectId也可以实现对象之间的关系:
// Add a relation between the Post with objectId "1zEcyElZ80" and the comment
[studentData setObject:[PFObject objectWithoutDataWithClassName:@"Course" objectId:@"1zEcyElZ80"]
forKey:@"registeredCourse"];
数据类型
Parse 支持所有数据类型格式,如NSString、NSData、NSNumber、PFObject、NSDate、NSNull和NSData。您甚至可以使用NSDictionary和NSArray的形式创建嵌套对象,以在单个PFObject中存储结构化数据。
让我们创建一些如下所示的随机变量:
NSNumber *number = [NSNumber numberWithInt:42];
NSString *string = [NSString stringWithFormat:@"the number is %i", number];
NSDate *date = [NSDate date];
NSData *data = [@"foo" dataUsingEncoding:NSUTF8StringEncoding];
NSArray *array = [NSArray arrayWithObjects:string, number, nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:number, @"number",string, @"string",
nil];
NSNull *null = [NSNull null];
PFObject *bigObject = [PFObject objectWithClassName:@"BigObject"];
[bigObject setObject:number forKey:@"myNumber"];
[bigObject setObject:string forKey:@"myString"];
[bigObject setObject:date forKey:@"myDate"];
[bigObject setObject:data forKey:@"myData"];
[bigObject setObject:array forKey:@"myArray"];
[bigObject setObject:dictionary forKey:@"myDictionary"];
[bigObject setObject:null forKey:@"myNull"];
[bigObject saveInBackground];
处理查询
要从云端获取保存的数据,我们可以使用查询来获取所需的数据。
让我们从简单的查询开始。我们可以通过执行以下简单的代码行异步地从 Parse 获取数据:
PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
// Retrieve the object by id
[query getObjectInBackgroundWithId:@"xWMyZ4YEGZ" block:^(PFObject *gameScore, NSError *error) {
// Now let's update it with some new data. In this case, only cheatMode and score
// will get sent to the cloud. playerName hasn't changed.
[gameScore setObject:[NSNumber numberWithBool:YES] forKey:@"cheatMode"];
[gameScore setObject:[NSNumber numberWithInt:1338] forKey:@"score"];
[gameScore saveInBackground];
}];
假设您想在从 Parse 获取结果时过滤结果,您可以在查询对象上添加条件,并检索过滤后的PFObject的NSArray。您可以使用以下方法从 Parse 云端查找数据:
findObjectsInBackgroundWithBlock:
或者您可以使用:
findObjectsInBackgroundWithTarget:selector:
这两种方法都是异步的,将在主线程上执行回调。有一个名为[query findObjects]的方法;此方法将阻塞执行它的线程。
例如,如果您想获取注册 MBA 课程的学生的列表,我们可以使用以下代码:
PFQuery *query = [PFQuery queryWithClassName:@"Student"];
[query whereKey:@"registeredCourse" equalTo:@"MBA"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(@"Successfully retrieved list of %d students.", objects.count);
} else {
// Log details of the failure
NSLog(@"Error: %@ %@", error, [error userInfo]);
}
}];
使用谓词
要向查询结果添加约束,我们可以使用以下方法:
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"studentName = 'Jack Samuel'"];
PFQuery *query = [PFQuery queryWithClassName:@"Student" predicate:predicate];
Parse 支持以下类型的谓词功能:
-
您可以使用与键和常量一起使用的比较,如=、!=、<、>、<=、>=和 BETWEEN
-
包含谓词,例如 x IN
-
存在性谓词,例如 x IN SELF
-
以 BEGINWITH 开头的表达式
-
使用 AND、OR 和 NOT 组合的复合谓词
-
使用“key IN %@”的子查询,subquery
以下类型的谓词不受 Parse 支持:
-
聚合操作,例如 ANY、SOME、ALL 或 NONE
-
正则表达式,如 LIKE、MATCHES、CONTAINS 或 ENDSWITH
-
比较两个键的谓词
-
具有许多 OR 子句的复杂谓词
添加查询约束
有多种方法可以添加查询结果的约束。以下是一些实现约束的方法:
-
为了找到查询中排除的指定学生之外的所有学生,Parse 提供了一个
whereKey:notEqualTo:方法来从 Parse 云端获取选择性过滤数据。此方法有多种组合方式。以下代码将获取学生姓名不是 John Simon 的对象:[query whereKey:@"studentName" notEqualTo:@"John Simon"]; -
我们可以在同一个查询中添加多个约束,结果对象将匹配所有约束:
[query whereKey:@"studentName" notEqualTo:@"John Simon"]; [query whereKey:@"studentAge" greaterThan:[NSNumber numberWithInt:18]]; -
您可以通过设置查询对象的
limit来限制查询的结果:query.limit = 10; -
如果您只想获取结果中的第一个对象,可以使用
getFirstObject或getFirstObjectInBackground。这两种方法都将返回从响应中接收到的对象数组中的第一个对象:PFQuery *query = [PFQuery queryWithClassName:@"Student"]; [query whereKey:@"studentName" equalTo:@"John"]; [query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) { if (!object) { NSLog(@"The getFirstObject request failed."); } else { // The find succeeded. NSLog(@"Successfully retrieved the object."); } }]; -
您可以通过设置
skip来跳过结果。这也可以用于分页:query.skip = 10; // skip the first 10 results -
Parse 提供了一个简单的方法来排序数字和字符串。它允许您控制返回结果的顺序:
// Sorts the results in ascending order by the name field [query orderByAscending:@"studentName"]; // Sorts the results in descending order by the name field [query orderByDescending:@"studentName"]; -
您可以通过在查询中提供比较来过滤结果:
// Fetch list of students who scored < 50 [query whereKey:@"score" lessThan:[NSNumber numberWithInt:50]]; // Fetch list of students who scored <= 50 [query whereKey:@"score" lessThanOrEqualTo:[NSNumber numberWithInt:50]]; -
您可以根据不同的值过滤结果。您可以为结果提供应出现在结果中的值的数组:
// Finds scores from any of Jonathan, Dario, or Shawn NSArray *names = [NSArray arrayWithObjects:@"Jonathan Walsh", @"Dario Wunsch",@"Shawn Simon",nil]; [query whereKey:@"studentName" containedIn:names]; Fetch objects excluding the data provided in the array. // Finds scores from anyone who is neither Jonathan, Dario, nor Shawn NSArray *names = [NSArray arrayWithObjects:@"Jonathan Walsh",@"Dario Wunsch",@"Shawn Simon",nil]; [query whereKey:@"studentName" notContainedIn:names];
关系和复杂查询
您可以通过在关系模式上执行操作来触发查询。以下是在关系模型下从 Parse 获取数据的各种方式:
基于数组的查询
如果您有数组格式的数据键,您可以找到包含值x的对象:
// Find objects where the array in arrayKey contains 2.
[query whereKey:@"arrayKey" equalTo:[NSNumber numberWithInt:2]];
如果您想获取匹配数组多个值的匹配数据,可以使用以下代码:
// Find objects where the array in arrayKey contains each of the
// elements 2, 3, and 4.
[query whereKey:@"arrayKey" containsAllObjectsInArray:@[@2, @3, @4]];
基于字符串的查询
您可以使用where:hasPrefix:在查询结果中添加约束:
// Finds student name that start with "Stuart".
PFQuery *query = [PFQuery queryWithClassName:@"Student"];
[query whereKey:@"name" hasPrefix:@"Stuart"];
关系查询
您可以从 Parse 中获取与特定 PFObject 字段匹配的对象。Parse 提供了一个whereKey:equalTo:方法来从查询中获取数据。假设您想获取已报名 MBA 课程的学生名单:
// Assume PFObject *myPost was previously created.
PFQuery *query = [PFQuery queryWithClassName:@"Student"];
[query whereKey:@"course" equalTo:myCourse];
[query findObjectsInBackgroundWithBlock:^(NSArray *studentList, NSError *error) {
// list of student enrolled for MBA course
}];
如果您想在单个查询中获取多种相关对象,Parse 提供了一个includeKey:方法来获取这类关系结果。例如,如果您想获取得分最高的学生名单,并且想同时获取他们的课程,您可以使用以下代码片段来获取所需结果:
PFQuery *query = [PFQuery queryWithClassName:@"student"];
// Retrieve the top scorer
[query orderByAscending:@"score"];
// Only retrieve the top ten
query.limit = [NSNumber numberWithInt:10];
// Include the course data with each student score
[query includeKey:@"course"];
[query findObjectsInBackgroundWithBlock:^(NSArray *students, NSError *error) {
// Top ten scorer Students from all courses
}];
计数对象
在某些情况下,您可能只需要知道结果数组的计数。为此,您不需要使用findObjects方法查询完整数据。Parse 提供了一个countObjects方法来获取 Parse 上查询数据的计数:
PFQuery *query = [PFQuery queryWithClassName:@"Student"];
[query whereKey:@"studentName" equalTo:@"Sean"];
[query countObjectsInBackgroundWithBlock:^(int count, NSError *error) {
if (!error) {
// The count request succeeded. Log the count
} else {
// The request failed
}
}];
复合查询
如果您想获取多个查询的结果对象,Parse 提供了一个orQueryWithSubqueries:方法来获取这类结果。
假设您需要获取得分最高和最低学生的名单;以下代码片段给出了所需输出:
// Query to fetch the list of students with highest score
PFQuery *highestScorer = [PFQuery queryWithClassName:@"Student"];
[lotsOfWins whereKey:@"score" greaterThan:[NSNumber numberWithInt:150]];
// Query to fetch the list of students with lowest score
PFQuery *lowestScorer = [PFQuery queryWithClassName:@"Player"];
[fewWins whereKey:@"score" lessThan:[NSNumber numberWithInt:5]];
PFQuery *query = [PFQuery orQueryWithSubqueries:[NSArray arrayWithObjects:highestScorer,lowestScorer,nil]];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
// results contains students with highest and lowest score.
}];
缓存查询
缓存数据是一个过程,我们将从远程 API 调用接收到的数据临时存储在设备上。因此,对于相同的请求,你不需要反复获取数据。缓存通过最小化的 API 调用提高了效率、性能,并减少了服务器的负载。
一旦通过查询从 Parse 请求数据,建议缓存它,以减少对 Parse 云的访问次数。你可以在磁盘上缓存结果。此功能将帮助你以离线模式显示数据或在应用程序刚刚启动且数据尚未获取时。
默认情况下,PFQuery 不缓存查询;然而,你可以通过设置属性 query.cachePolicy 来启用 PFQuery 的缓存。让我们举一个例子,如果网络可达性不可用,你可以添加一个自动回退到缓存数据:
PFQuery *query = [PFQuery queryWithClassName:@"Student"];
query.cachePolicy = kPFCachePolicyNetworkElseCache;
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// Results were successfully found, looking first on the
// network and then on disk.
} else {
// The network was inaccessible and we have no cached data for
// this query.
}
}];
Parse 为你提供了以下缓存策略:
kPFCachePolicyIgnoreCache |
这是默认的缓存策略。查询既不加载缓存也不将结果保存到缓存中。 |
|---|---|
KPFCachePolicyCacheOnly |
查询只从缓存加载,忽略网络。如果没有缓存结果,将导致 PFError。 |
KPFCachePolicyNetworkOnly |
它不加载缓存,但将结果保存到缓存中。 |
KPFCachePolicyCacheElseNetwork |
它尝试从缓存加载,但如果查询失败,则从网络加载结果。如果缓存和网络都未成功,则会有 PFError。 |
KPFCachePolicyNetworkElseCache |
这个查询首先尝试从网络加载,如果失败,则从缓存中加载结果。如果网络和缓存都未成功,你将得到一个 PFError。 |
KPFCachePolicyCacheThenNetwork |
这个查询首先从缓存加载,然后从网络加载。在这种情况下,回调将被调用两次——首先使用缓存结果,然后使用网络结果。由于它在不同时间返回两个结果,因此此缓存策略不能与 findObjects 同步使用。 |
可以使用以下操作在缓存上控制缓存行为:
-
它帮助你检查查询的缓存结果:
BOOL isInCache = [query hasCachedResult]; -
它清除查询对象的缓存结果:
[query clearCachedResult]; -
它清除
PFQuery的所有缓存:[PFQuery clearAllCachedResults]; -
你可以控制缓存结果存在的时长:
query.maxCacheAge = 60 * 60 * 24; // One day, in seconds.
摘要
在本章中,我们探讨了 Parse 对象以及查询 Parse 上可用数据的方式。
我们首先探索了 Parse 对象以及将这些对象保存到云上的方法。
然后,我们学习了查询,这些查询将帮助我们从 Parse 获取保存的数据。
最后,我们看到了实现查询和约束的多种方式,这些方式可以帮助我们减少查询的复杂性。
在下一章中,我们将学习在 Parse 云上保存我们的文件的各种方法,以及 Parse 对象的子类化概念。
第三章:子类和文件
Parse 为你提供了快速即插即用的方法来开始你的应用程序开发。你可以使用 PFObject 在 Parse 上保存和检索数据,并且你可以使用 objectForKey 方法访问对象中的任何数据字段。为了减少代码的复杂性,同时提高代码的可读性和可扩展性,你可以为你的 PFObject 使用子类。这也帮助你将应用程序架构调整为模型视图控制器架构。在本章中,我们将学习如何子类化 PFObject 以及在 Parse 云上保存文件的方法。
子类化 PFObject
PFObject 可以被子类化以提供代码的灵活性。基本上,它帮助你以面向对象的方式塑造后端代码,以提高可读性和应用程序架构。它还有助于使你的代码可重用。例如,你可以通过以下传统方式在 Parse 上保存你的数据:
// Create/Save course data
PFObject *myCourse = [PFObject objectWithClassName:@"Course"];
[myCourse setObject:@"MBA" forKey:@"courseName"];
[myCourse setObject:@"2 years" forKey:@"courseDuration"];
[myCourse saveInBackground];
子类化之后,你可以将之前的代码转换为以下代码:
Course *courseObject = [Course object];
courseObject.courseName = @"MBA";
courseObject.courseDuration = @"2 years";
[courseObject saveInBackground];
通过子类化 PFObject 创建 Course 对象,你可以轻松地与业务对象交互。此外,通过在代码中使用属性,它增加了代码库的可读性,从而简化了你的代码架构。
执行以下步骤以创建 PFObject 的子类:
-
创建一个
PFObject的子类以遵循 PFSubclassing 协议。 -
实现类方法
parseClassName。此方法将返回作为string的类名,你将传递给initWithClassName:。 -
实现文件(
.m文件)导入PFObject + Subclass。这个类包括 PFSubclassing 中的所有方法。 -
在
ApplicationDelegate中的userInfo setApplicationId:clientKey:调用之前,调用[YourClass registerSubclass]方法。 -
以下代码演示了
PFObject的Course子类:// Course.h @interface Course : PFObject<PFSubclassing> + (NSString *)parseClassName; @end // Course.m // Import this header to let your Course class know that PFObject privately // provides most of the methods for PFSubclassing. #import <Parse/PFObject+Subclass.h> @implementation Course + (NSString *)parseClassName { return @"Course"; } @end // AppDelegate.m #import <Parse/Parse.h> #import "Course.h" - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Course registerSubclass]; [Parse setApplicationId:parseAppId clientKey:parseClientKey]; }
自定义属性和方法
为了封装你的自定义业务逻辑,你可以在你的 PFObject 子类中添加自定义属性和方法。通过使用 PFSubclassing 方法,你可以将代码简化并组织在一个地方,而不是让它散布在不同的类中。PFObject 支持属性的动态合成。你可以像通常声明属性一样声明属性;然而,这次你需要在实现文件中使用 @dynamic 代替 @synthesis。动态属性告诉编译器设置器和获取器方法不是由类实现的,而是在某个地方作为超类实现的。假设你需要在你 Course 类中添加一个属性。以下代码将属性添加到你的 Course 类中:
// Course.h
@interface Course : PFObject<PFSubclassing>
+ (NSString *)parseClassName;
@property (nonatomic, strong) NSString *courseName;
@property (nonatomic, strong) NSString *courseDuration;
@end
// Course.m
@dynamic courseName;
@dynamic courseDuration;
你可以使用 course.courseName 或 [course courseName] 来访问 courseName 和 courseDuration 属性,而如果你想设置数据到属性,你可以使用 course.courseName = @"MBA" 或 [course setCourseName:@"MBA"]。
如果你使用 NSNumber 作为数据类型,你可以按以下方式实现:
@property BOOL isGameOver;
@property float amount;
在获取数据时,你可以使用course [object objectForKey:@"isGameOver"],这将返回NSNumber,你可以通过boolValue来访问它。而在处理金额时,你可以使用floatValue方法来访问。动态获取器方法会自动将float或bool值转换为,而动态设置器方法会将所有原始数据类型方法封装在NSNumber下。
初始化子类
使用object类方法创建新对象。这将自动创建一个提供类型的自动释放实例,并处理进一步的子类化。你可以使用这个objectWithoutDataWithObjectId:方法来创建当前对象的引用:
PFObject *parseObject = [PFObject objectWithoutDataWithClassName:@"Course" objectId:courseObject.objectId];
上一行代码将创建一个对现有 Parse 对象的引用;在我们的例子中是Course。
创建查询
你可以使用query类方法为子类对象创建查询。Parse 为你提供了PFQuery类,它允许你在 Parse 云上获取和保存数据。PFQuery 为你提供了许多设置和获取 Parse 云上数据的方法。假设你需要为你的Course类创建一个查询,你需要使用以下方法来创建查询:
PFQuery *query = [Course query];
[query whereKey:@"courseName" isEqualTo:PFUser.currentUser.courseName];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
Course *firstCourse = [objects objectAtIndex:0];
// Your code here
}
}];
之前的代码创建了一个查询,使用courseName过滤器来获取你的Course类数据。你将得到一个当前用户订阅的所有课程的数组。此操作将异步执行,因为我们是在后台线程上执行此操作。PFQuery 为你提供了各种方法来获取和保存 Parse 云上的数据。
处理文件
在应用程序中,有时我们需要在数据库或后端存储我们的文件和数据。文件可以是任何类型,可以是我们的图片文件、视频文件、音频文件或数据文件。Parse 为我们提供了即插即用的功能,以便在云上保存数据和文件。
PFFile
Parse 应用程序可以支持任何类型的文件,如图片、视频文件、音频文件或其他类型的数据文件,但它们的大小应该小于 10 兆字节。PFFile 帮助你将应用程序相关的文件存储在 Parse 云上。
为了在云上保存数据,你需要将你的文件转换为 NSData。然后你可以从该数据创建一个 PFFile 对象,并将 NSData 传递给 PFFile 对象以在云上保存:
NSData *data = [@"Hello world!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *exampleFile = [PFFile fileWithName:@"brochure.txt" data:data];
PFFile 提供了fileWithName: data:方法来在 Parse 云上保存你的文件。你可以在 Parse 上保存任何类型的文件,如图片、文本文件和数据文件。在将文件保存到 Parse 之前,你需要将所有文件转换为 NSData。在之前的代码中,我们在将Hello world!字符串保存到 Parse 云之前将其转换为 NSData。文件名应该是 NSString 格式,在我们的例子中,我们使用了brochure.txt作为文件名。
在之前的代码行中,我们使用了文件名demo.txt。请记住以下关键点:
-
您无需担心云端的文件名冲突,因为 Parse 会自动处理这些事情,为所有文件提供唯一的标识符,因此您可以拥有多个同名文件。
-
建议为您的数据文件提供扩展名,因为这有助于 Parse 理解您的文件格式并适当处理文件。因此,在存储图片时,您应使用
.png或.jpg作为扩展名。
现在,您可以将文件保存到云端。所有保存方法都适用于 PFFile。您可以使用适合您需求的任何方法:
[file saveInBackground];
在成功保存您的文件后,您可以像关联其他数据一样将您的文件与 PFObject 关联:
PFObject *parseObject = [PFObject objectWithClassName:@"Course"]
[parseObject setObject:@"MBA" forKey:@"courseName"];
[parseObject setObject:exampleFile forKey:@"brochure"];
[parseObject saveInBackground];
上一行的代码将您的 brochure 文件与 Course 类关联。您可以使用以下代码从 Parse 获取相同的文件:
PFFile *fetchObj = [parseObject objectForKey:@"brochure"];
NSData *resumeData = [fetchObj getData];
要从 Parse 云端获取数据,您可以使用 getData: 方法及其变体。Parse 为您提供了 getData 方法的所有变体,就像我们之前看到的将 PFObject 保存到云端一样。
保存图片
要将您的图片保存到云端,您必须将您的图片转换为 NSData,然后您可以将这些数据保存到云端。假设您有一个图片,您需要将其保存到云端,那么您应该执行以下步骤:
UIImage *imageObj = [UIImage imageNamed:@"picture.png"];
NSData *imageData = UIImagePNGRepresentation(imageObj);
PFFile *imageFile = [PFFile fileWithName:@"picture.png" data:imageData];
[imageFile save];
PFObject *userPhoto = [PFObject objectWithClassName:@"UserPhoto"];
[userPhoto setObject:@"Test Image" forKey:@"imageName"];
[userPhoto setObject:imageFile forKey:@"imageFile"];
[userPhoto save];
在上一行的代码中,我们首先将一个图片文件转换为 NSData。然后我们创建了一个具有该数据作为 picture.png 图像键值的 PFFile 对象。然后我们在 PFFile 对象上调用 save 函数来保存文件。因此,现在这个 PFFile 对象可以用作任何其他 Parse 对象的值。接下来,我们在 UserPhoto 类上创建了一个对象,并将 PFFile 对象作为我们的 imageFile 键的值保存。然后最终我们在 UserPhoto Parse 对象上调用了 save 方法。
跟踪进度
Parse 帮助您通过以下提供的方法使用您的 PFFile 跟踪上传和下载的进度:
-
使用以下方法,您可以跟踪您在云端保存的数据的进度:
saveInBackgroundWithBlock:progressBlock -
使用以下方法,您可以在从云端获取数据时跟踪进度:
getDataInBackgroundWithBlock:progressBlock
让我们通过一个例子来说明如何使用您的应用程序进行进度跟踪:
NSData *data = [@"Progress tracking of files is Easy!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *file = [PFFile fileWithName:@"brochure.txt" data:data];
[file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// Handle success or failure here ...
} progressBlock:^(int percentDone) {
// Update your progress spinner here. percentDone will be between 0 and 100.
}];
概述
在本章中,我们探讨了将我们的数据保存到 Parse 的各种方法。数据可以是任何东西,例如图片文件、音频文件、视频文件或简单的二进制数据文件。
我们首先探讨了子类化我们的 PFObject 的步骤。然后我们学习了 PFFile 类,它允许我们将数据保存到云端。我们还学习了将数据保存到云端以及将图片保存到云端的方法。
最后,我们看到了跟踪我们文件进度的各种方法。
在下一章中,我们将学习关于 Parse 分析以及跟踪您应用程序的各种方法。
第四章:Parse 分析
分析在应用程序开发中扮演着重要的角色。分析为您提供数据,并显示它如何影响客户基础。分析帮助组织和管理层做出重要的决策,以向前推进并制定商业计划和策略。您可以使用分析来获取客户行为的详细知识。
Parse 为您提供了内置的分析支持。这是一种即插即用的分析方法,用于跟踪您的应用程序。
Parse 分析将帮助您跟踪应用程序,并有助于做出使应用程序成功的管理决策。在本章中,我们将学习关于 Parse 分析的集成以及如何使用这些分析生成数据的各种方法。
集成 Parse 分析
要在 Parse 上跟踪您的应用程序,您需要在 applicationDelegate 文件中的 application:didFinishLaunching: 方法中插入以下代码:
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
通过添加前面的代码行,您可以收集有关应用程序打开频率及其触发原因的数据。这还将包括应用程序打开的方式和推送通知的影响。
如果您将 launchOptions 参数传递为 nil,则应用程序将仅跟踪标准的应用程序打开事件。如果您的应用程序正在后台运行,则您的 application:didReceiveRemoteNotification: 方法将被调用。在这种情况下,您需要在该方法中添加以下代码:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (application.applicationState == UIApplicationStateActive) {
// The application was already running.
} else {
// The application was just brought from the background to the foreground,
// so we consider the app as having been "opened by a push notification."
[PFAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo];
}
}
一旦您收到应用程序的推送通知,您的应用程序徽章数量会增加,并且在从推送通知启动应用程序后,您需要清除徽章。清除徽章的代码在以下部分中。
跟踪页面
Parse 允许您跟踪您的应用程序页面。这有助于您了解用户访问您页面的频率。Parse 提供了 trackEvent: 方法来跟踪您应用程序中的页面。假设您想跟踪您的文章页面被访问的频率。您可以使用以下代码来跟踪此事件的频率:
[PFAnalytics trackEvent:@"article_page"];
在某些情况下,我们需要跟踪带有特定数据的页面。例如,在搜索的情况下,跟踪哪些数据被搜索得最多是有帮助的。因此,为了跟踪带有特定数据的事件,您可以使用 trackEvent:dimensions: 方法。此方法接受一个字典作为参数,允许您在跟踪页面时传递特定数据。假设我们想跟踪用户最常阅读的文章。在这种情况下,您可以使用以下代码:
NSDictionary *dimensions = @{
// Define the article name
@"articleName": @"advantages of parse",
// Provide the topic category
@"category": @"technology",
};
// Send the dimensions to Parse along with the 'article' event
[PFAnalytics trackEvent:@"article" dimensions:dimensions];
之前的代码将帮助您跟踪事件以及自定义参数。
清除所有徽章
要在应用程序打开时清除所有徽章,您需要设置当前安装的属性以更新徽章数量,并确保在保存时更新徽章值。为了实现所有场景,您需要插入以下代码行:
- (void)applicationDidBecomeActive:(UIApplication *)application {
PFInstallation *obj = [PFInstallation currentInstallation];];
if (obj.badge != 0) {
obj.badge = 0;
[obj saveEventually];
}
// Your code here
}
您可以从 Parse 应用程序仪表板访问分析数据。在那里,您将找到根据您的需求过滤数据的选择,这将有助于根据跟踪标准跟踪应用程序。
读取分析数据
Parse 分析为您提供访问各种类型数据分析的能力。您可以根据以下过滤器过滤您的分析数据:
-
推送通知:此过滤器可以帮助您跟踪由应用程序发送到设备的推送通知。
-
应用打开:此过滤器可以帮助您跟踪您的应用程序被打开的频率。您甚至可以根据时间过滤您的数据。您可以获取任何特定日期、月份或周的数据。
-
API 请求:如图所示,您可以将API 请求过滤器添加到应用程序中。此过滤器可以帮助您跟踪在指定时间段内发出的 API 请求数量:
![读取分析数据]()
-
应用程序平台:此过滤器将帮助您跟踪访问应用程序的设备,例如 iOS、Android、Windows 等。
-
所有类:如图所示,这将根据我们在应用程序中访问的类过滤结果,以及在我们应用程序生命周期中对该类所做的更改:
![读取分析数据]()
-
请求:这将根据请求的类型过滤数据,例如创建、获取、删除、查找、更新和查找。基于此类请求,我们可以跟踪在应用程序上执行了哪些操作。
应用过滤器后,您将获得一个图表,这将帮助您提取指定事件发生的频率:

您可以设置需要访问数据的时间段。数据可以以图表格式或条形图格式显示。
数据使用
传统上,公司使用数据根据历史证据预测趋势。现在您可以通过分析找到数字背后的含义,并根据相关事件区分数据。数据图表允许您制定商业策略。例如,用户在其应用程序上花费的总时间,以及如何通过微小变化来改善他们的结果。分析通过提供数据上下文来节省时间;这有助于我们最大化生产力并优先处理任务。
一旦访客开始使用应用程序,您就可以确定哪些内容来源获得了最多的流量,以及流量的频率。这些信息可用于确定哪些内容对应用程序用户更有价值,并有助于根据访客偏好更有效地开发内容。
分析的优势
在移动应用程序中使用分析具有各种优势,例如:
-
分析帮助您跟踪您的移动应用程序以提高用户满意度
-
它可以帮助你处理数据,并利用这些数据构建业务发展模型。
-
分析可以帮助你了解你的应用用户及其兴趣领域,例如应用中最常访问的区域是哪个。
摘要
在本章中,我们探讨了 Parse 分析以及将其集成到我们项目中的方法。
我们首先在项目中集成了 Parse 分析功能。然后我们学习了如何从 Parse 仪表板读取分析数据。最后,我们看到了在移动应用中使用分析的一些优势。
在下一章中,我们将学习如何配置和向用户发送推送通知。
第五章。推送通知
推送通知允许您在应用处于后台或根本未运行时发送消息。推送通知增加了用户与应用的互动,并通过消息让所有用户了解应用,从而产生收入。此外,通过将您的数据和推送服务相互连接,您可以以选择性的方式发送推送通知,例如祝贺您与 Packt Publishing 合作满一年。这是接触所有用户的最简单方法。Parse 为您提供推送通知服务。本章将帮助您为您的应用配置推送通知。
设置推送通知
通知帮助您在应用中添加实时消息。为此,您需要创建 SSL 证书,然后将创建的证书与您的App ID和配置文件关联。
创建 SSL 证书
您需要在 Apple 开发者门户上创建您的App ID及其关联的 SSL 证书。Parse 服务器将使用此证书向由该App ID标识的应用发送推送通知。
生成证书请求
执行以下步骤以生成证书并将其保存到您的磁盘:
-
在您的 Mac 上打开钥匙串访问应用程序。
-
导航到钥匙串访问 | 证书助手 | 从证书颁发机构请求证书…。
-
在证书信息窗口中,输入您的电子邮件地址和姓名。
-
选择保存到磁盘以将.certSigningRequest文件下载到您的桌面。
设置您的 App ID
所有安装在开发者设备上的 iOS 应用都需要 App ID。您可以使用现有的 App ID,但请确保它没有通配符。您可以通过以下步骤创建新的 App ID:
-
登录到 Apple 开发者门户(
developer.apple.com/membercenter/index.action)并导航到证书、标识符和配置文件(developer.apple.com/account/overview.action)。 -
点击门户左侧面板中的标识符。
-
点击右上角的+按钮以创建新的 App ID。
-
输入新 App ID 的名称,并在应用服务部分下选择推送通知复选框。
-
在App ID 后缀部分,选择显式 App ID。输入您的 iOS 应用的Bundle ID。此字符串应与 iOS 应用
info.plist文件中的 Bundle 标识符相匹配。 -
点击继续然后点击提交以完成注册。
-
从iOS App ID列表中选择您新创建的 App ID,然后点击设置。
-
导航到推送通知部分。您可以下载开发和生产SSL 证书。
-
点击 创建证书...,然后点击 继续。
-
现在,你可以上传上一节中生成的证书,然后点击 生成。
-
上传你的证书后,点击 完成,然后从 iOS App ID 设置 屏幕下载生成的 SSL 证书。
-
通过双击 SSL 证书在你的密钥链中安装下载的证书。
-
在 密钥链访问 中,在 我的证书 部分选择你安装的证书,它应该以 Apple Development IOS Push Services: 作为前缀。
-
右键单击选定的证书并将其保存为
.p12文件格式。在提示时不要输入任何密码。
创建你的配置文件
为了验证你的设备以用于应用程序,你需要创建一个配置文件。每次更新你的 App ID 时,你都需要重新生成你的配置文件。按照以下步骤创建配置文件:
-
登录到苹果开发者门户并导航到 证书、标识符和配置文件 部分。
-
从 iOS 应用 部分选择 配置文件。
-
点击该部分右上角的 + 按钮,以添加一个新的配置文件。
-
选择你的配置文件类型为 iOS 应用开发 并点击 继续。
-
选择上一节中创建的应用 ID 并点击 继续。
-
选择你的证书并点击 继续。
-
从设备列表中选择你想要安装和测试应用程序的设备,并点击 继续。
-
为你的配置文件提供一个名称并点击 生成。
-
下载生成的配置文件并双击以安装它。
这应该会在 设备 面板中启动你的 Xcode 的 组织者。你的新配置文件应该出现在 库 的 配置文件 部分中。确保配置文件的 状态 是 有效 的。
配置你的 Parse 应用
要使用 Parse 的推送通知服务,你必须在 Parse 应用中启用此功能,通过上传上一节中生成的推送 SSL 证书:
-
导航到你的 Parse 应用在 Parse 仪表板上,并选择 设置 选项卡。
-
在设置面板的左侧,你将看到 推送通知。在 Apple 推送证书 标题下,点击 选择你的证书 并定位到上一节从密钥链导出的
.p12证书。
配置 iOS 应用以使用推送通知
你需要更新以下应用程序设置以配置推送通知:
-
你的应用程序包标识符应该与你创建 App ID 时在苹果开发者门户上提供的完全相同。
-
更新你的 代码签名标识符 字段以匹配上一节中创建的配置文件。
安装
在所有前面的部分中,我们已经配置了开始使用推送通知所需的所有内容。在本节中,我们将添加一些代码以在我们的应用程序中接收推送通知。
您需要注册您的设备以获取推送通知,在应用的代理方法 [application:didFinishLaunchingWithOptions:] 中添加以下代码。
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
// Register for push notifications
[application registerForRemoteNotificationTypes:
UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound];
...
}
在成功注册后,您的应用代理中的回调方法 [application:didRegisterForRemoteNotificationsWithDeviceToken:] 将被执行。我们需要配置此方法以通知 Parse 关于新设备的信息:
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:newDeviceToken];
[currentInstallation saveInBackground];
}
您可以像更新 PFObject 一样更新 PFInstallation。您可以添加各种特殊字段,这些字段将帮助您在 Parse 上管理您的设备。以下是一些字段及其用途:
-
badges: 您可以在PFInstallation中更改此值以更新应用图标的徽章。建议在服务器上保存徽章更改,以便将来进行徽章增量通知。 -
channels: 这将存储订阅当前设备的频道数组。 -
timeZone: 此属性用于设备的当前位置。当我们更新服务器上的Installation对象时,它会自动更新。这是一个只读属性。 -
deviceType: 此属性适用于任何设备类型,可以是 iOS、Android、winrt、winphone 或 dotnet。这是一个只读属性。 -
installationId: 这是 Parse 用于设备的唯一 ID 属性。这是一个只读属性。 -
deviceToken: 这是用于 iOS 设备的 Apple 生成的令牌。这是一个只读属性。 -
channelUris: 这是 Microsoft 为 Windows 设备生成的推送 URI。这是一个只读属性。 -
appName: 这是属于此安装的客户端应用程序的显示名称。这是一个只读属性。 -
appVersion: 这是属于当前安装的客户端应用程序的版本字符串。这是一个只读属性。 -
parseVersion: 这是使用此安装的 Parse SDK 的版本。这是一个只读属性。 -
appIdentifier: 这是此安装客户端应用程序的唯一标识符。在 iOS 中,这是Bundle Identifier。这是一个只读属性。
当您的应用程序在前台模式下接收到通知时,您可以使用以下代码来处理通知:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
[PFPush handlePush:userInfo];
}
在接收到通知后,我们可以将通知传递给 Parse 以处理。它将自动创建一个模态警报,并显示推送消息内容。
您现在可以在您的设备上执行应用程序以确保一切设置正确。如果您是第一次在您的设备上运行此应用,您将收到一个模态框请求用户发送推送通知的权限。
发送推送通知
您可以使用以下方式向设备发送推送通知。
Parse 网站
执行以下步骤通过 Parse 网站发送通知:
-
导航到您的 Parse 应用程序,位于 Parse.com 上。
-
点击推送通知标签。
-
您可以点击框的右上角的发送推送。
-
在消息框中编写一条消息,并将其广播给所有人。您还有其他选项可供选择设备类型。
-
Parse 允许您安排通知。您可以配置此计划并点击发送通知来发送通知。
Parse 应用程序
通常,您可以通过 Web 控制台、使用 REST API 或 Cloud Code 发送推送通知。然而,Parse 还允许您从您的移动应用程序发送推送通知。请记住,您已将客户端推送设置为是。启用的客户端推送设置允许您从一个设备向另一个设备发送通知。PFPush类中提供了许多方法,您可以通过这些方法通过您的移动设备发送推送通知。您可以在 API 文档中访问所有这些方法。以下是从 iOS 设备生成的推送通知的示例代码:
// Create our Installation query
PFQuery *pushQuery = [PFInstallation query];
[pushQuery whereKey:@"deviceType" equalTo:@"ios"];
// Send push notifications to query
[PFPush sendPushMessageToQueryInBackground:pushQuery
withMessage:@"Hello World!"];
Cloud Code
Cloud Code 还允许您使用Parse.Cloud.afterSave方法发送推送通知,该方法将在对象在 Parse 上成功保存后执行。Cloud 可以用 JavaScript 编写。您应该熟悉 JavaScript 才能编写 Cloud Code。让我们以发送每条评论后发送推送通知的示例为例:
Parse.Cloud.afterSave("Comment", function(request) {
// Our "Comment" class has a "text" key with the body of the comment itself
var commentText = request.object.get('text');
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.equalTo('deviceType', 'ios');
Parse.Push.send({
where: pushQuery, // Set our Installation query
data: {
alert: "New comment: " + commentText
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
throw "Got an error " + error.code + " : " + error.message;
}
});
});
发送通知
使用 Parse 向设备发送推送通知有两种方式。如下所示。
通道
这是开始发送通知的最简单方法。它基于发布/订阅模型。例如,如果您想向用户发送关于他/她创建的每条评论的通知,那么您必须创建一个通道并将用户订阅到该通道,并且对于每条评论您都需要为该通道发送一个推送。这将使用户知道当有人评论他/她的帖子时,即使应用程序未运行或处于后台。因此,为了发送推送通知,您需要允许用户创建一个通道。一个设备可以订阅一个或多个通道以接收可以发送给此类订阅者的通知。订阅的通道存储在Installation对象的channels字段中。
订阅通道
您可以通过字符串识别一个通道,这可以是由字母数字字符、下划线和破折号组成的组合。每个安装可以在任何时候订阅任意数量的通道。您可以通过在Installation对象中使用以下代码来添加一个通道:
// When users Comment, we subscribe them to that channel.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObject:@"Comments" forKey:@"channels"];
[currentInstallation saveInBackground];
在订阅评论后,您的Installation对象应类似于以下内容:
objectId:yvoZDtAxUR channels:["Comments"]
要从通道中取消用户订阅,您可以添加以下代码:
// When users indicate they are no longer Giants fans, we unsubscribe them.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation removeObject:@"Comments" forKey:@"channels"];
[currentInstallation saveInBackground];
为了缓存目的,获取通道列表作为数组,您可以使用以下代码:
NSArray *subscribedChannels = [PFInstallation currentInstallation].channels;
向通道发送推送通知
要向所有订阅了Comments渠道的用户发送推送通知,以下代码将向所有订阅用户发送推送:
// Send a notification to all devices subscribed to the "Giants" channel.
PFPush *push = [[PFPush alloc] init];
[push setChannel:@"Comments"];
[push setMessage:@"Thats the new comment!"];
[push sendPushInBackground];
如果您想同时向多个渠道发送推送,可以使用以下代码:
NSArray *channels = [NSArray arrayWithObjects:@"Comments", @"Mets", nil];
PFPush *push = [[PFPush alloc] init];
// Be sure to use the plural 'setChannels'.
[push setChannels:channels];
[push setMessage:@"Multiple channel comments."];
[push sendPushInBackground];
使用高级定位
在高级推送通知中,Parse 允许您嵌入查询,并将查询结果作为推送通知发送给用户。这将帮助您向自定义动态用户群体发送推送。
您可以将数据保存到Installation对象中,就像任何PFObject一样。假设在您的应用程序中,您允许用户点赞、评论和发布。以下代码片段将帮助您实现这一点:
// Store app language and version
PFInstallation *installation = [PFInstallation currentInstallation];
[installation setObject:[NSNumber numberWithBool:YES] forKey:@"Like"];
[installation setObject:[NSNumber numberWithBool:YES] forKey:@"Comment"];
[installation setObject:[NSNumber numberWithBool:YES] forKey:@"Posts"];
[installation saveInBackground];
向查询发送推送
您可以通过对目标设备应用查询来过滤Installation对象。这可以通过以下代码实现:
// Create our Installation query
PFQuery *pushQuery = [PFInstallation query];
[pushQuery whereKey:@"Comments" equalTo:[NSNumber numberWithBool:YES]];
// Send push notifications to query
PFPush *push = [[PFPush alloc] init];
[push setQuery:pushQuery]; // Set our Installation query
[push setMessage:@"This is the test comment."];
[push sendPushInBackground];
在发送通知之前,我们可以将查询设置为PFPush。您还可以将数据存储在关系中。这些关系也可以用于查询:
// Find users near a given location
PFQuery *userQuery = [PFUser query];
[userQuery whereKey:@"location"
nearGeoPoint:partyLocation
withinMiles:[NSNumber numberWithInt:1]]
// Find devices associated with these users
PFQuery *pushQuery = [PFInstallation query];
[pushQuery whereKey:@"user" matchesQuery:userQuery];
// Send push notifications to query
PFPush *push = [[PFPush alloc] init];
[push setQuery:pushQuery]; // Set our Installation query
[push setMessage:@"Party tickets free to all nearby users!"];
[push sendPushInBackground];
自定义通知
您可以自定义推送通知数据,例如播放的声音、徽章编号、要发送的自定义数据和通知的过期时间。
您还可以通过将所有设置包装在NSDictionary方法中来自定义通知。以下是一个示例,通过通知增加徽章计数并添加自定义声音和消息:
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:
@"New comment!", @"alert",
@"Increment", @"badge",
@"demo.caf", @"sound",
nil];
PFPush *push = [[PFPush alloc] init];
[push setChannels:[NSArray arrayWithObjects:@"Mets", nil]];
[push setData:data];
[push sendPushInBackground];
要设置通知的过期日期,可以使用以下示例代码:
// Create date object for tomorrow
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setYear:2013];
[comps setMonth:8];
[comps setDay:27];
NSCalendar *gregorian =[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *date = [gregorian dateFromComponents:comps];
// Send push notifications with expiration date
PFPush *push = [[PFPush alloc] init];
[push expireAtDate:date];
[push setQuery:everyoneQuery];
[push setMessage:@"Movie tickets on sale until August 27th"];
[push sendPushInBackground];
基于平台的定位
您可以让应用程序在多个平台上运行。如果您想针对特定平台设备或操作系统发送推送,则可以使用以下代码。此代码将为 iOS、Android 和 Windows 应用程序发送单独的通知:
PFQuery *query = [PFInstallation query];
[query whereKey:@"channels" equalTo:@"suitcaseOwners"];
// Notification for Android users
[query whereKey:@"deviceType" equalTo:@"android"];
PFPush *androidPush = [[PFPush alloc] init];
[androidPush setMessage:@"Your suitcase has been filled with tiny robots!"];
[androidPush setQuery:query];
[androidPush sendPushInBackground];
// Notification for iOS users
[query whereKey:@"deviceType" equalTo:@"ios"];
PFPush *iOSPush = [[PFPush alloc] init];
[iOSPush setMessage:@"Your suitcase has been filled with tiny apples!"];
[iOSPush setChannel:@"suitcaseOwners"];
[iOSPush setQuery:query];
[iOSPush sendPushInBackground];
// Notification for Windows 8 users
[query whereKey:@"deviceType" equalTo:@"winrt"];
PFPush *winPush = [[PFPush alloc] init];
[winPush setMessage:@"Your suitcase has been filled with tiny glass!"];
[winPush setQuery:query];
[winPush sendPushInBackground];
// Notification for Windows 8 users
[query whereKey:@"deviceType" equalTo:@"winphone"];
PFPush *winPush = [[PFPush alloc] init];
[wpPush setMessage:@"Your suitcase is very hip; very metro."];
[wpPush setQuery:query];
[wpPush sendPushInBackground];
接收推送通知
当您收到自定义通知时,您可以从通知中检索相关数据。这些数据可以帮助根据通知修改应用程序的行为。例如,如果通知是因为对帖子的评论而发送的,您可以直接通过阅读推送通知数据来打开该评论的帖子。
苹果对包大小有限制,因此建议尽可能保持数据大小最小:
NSDictionary *data = @{
@"alert": @"James commented on your photo!",
@"p": @"vmRZXZ1Dvo" // Photo's object id
};
PFPush *push = [[PFPush alloc] init];
[push setQuery:photoOwnerQuery];
[push setData:data];
[push sendPushInBackground];
响应有效载荷
如果您的应用是通过通知启动的,您可以通过launchOptions字典从[application:didFinishLaunchingWithOptions:]方法访问数据:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
. . .
// Extract the notification data
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
// Create a pointer to the Post object
NSString *postId = [notificationPayload objectForKey:@"p"];
PFObject *targetPost = [PFObject objectWithoutDataWithClassName:@"Post"objectId:postId];
// Fetch photo object
[targetPhoto fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
// Show post view controller
if (!error && [PFUser currentUser]) {
PostVC *viewController = [[PostVC alloc] initWithPost:object];
[self.navController pushViewController:viewController animated:YES];
}
}];
}
如果您的应用程序已经在前台模式下运行,则可以从[application:didRegisterForRemoteNotificationsWithDeviceToken:]方法中的userInfo字典中获取数据:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
// Create empty post object
NSString *postId = [userInfo objectForKey:@"p"];
PFObject *targetPost = [PFObject objectWithoutDataWithClassName:@"Post"objectId:postId];
// Fetch photo object
[targetPhoto fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
// Show photo view controller
if (!error && [PFUser currentUser]) {
PostVC *viewController = [[PostVC alloc] initWithPost:object];
[self.navController pushViewController:viewController animated:YES];
}
}];
}
摘要
在本章中,我们探讨了在我们的应用程序中设置和安装推送通知。
我们首先在 Apple 开发者门户上设置了推送通知,并在代码库中进行了必要的安装。
然后,我们学习了发送推送通知及其调度。
最后,我们了解了从通知中接收有效载荷的方法。
在下一章中,我们将学习关于 Parse 用户及其角色的内容。
第六章:用户和角色
如今,大多数应用程序都在处理用户账户,并帮助用户安全地访问他们的数据。Parse 为您提供了一个专门封装了处理用户数据所需所有功能的 PFUser 类。所有应用程序通常都包含某种形式的用户。借助 Parse 和 PFUser 类,您可以在应用程序中轻松集成用户认证和数据存储的功能。此类帮助您在应用程序中添加用户账户功能。
PFUser 类继承自 PFObject,因此为您提供了标准 PFObject 的所有功能,例如灵活的数据馈送模式,以及基于字典的关键值关系。PFUser 可以访问 PFObject 的所有方法,除此之外,PFUser 还有一些额外的帮助管理账户信息的方法。
在本章中,您将详细了解如何处理用户,以及如何为用户提供角色以限制对数据的访问。
PFUser
此类允许您在 Parse 上管理用户账户。它有几个属性,使其与 PFObject 区分开来,如下所示:
-
username: 用户的用户名(必填) -
password: 用户的密码(注册时必填) -
email: 用户的电子邮件 ID(可选)
这些属性将内部调用 setObject:forKey: 方法,因此您不需要外部调用此方法。我们将在接下来的主题中详细探讨这些字段。
所有的 PFUser 对象都将存储在 User 类中。您可以在应用程序的 数据浏览器 中访问 User 类。此类还允许您为用户添加自定义字段,例如添加电话号码等相关细节。
用户登录
在大多数应用程序中,第一步是拥有一个有效的登录过程。Parse 为您提供了一个稳定的即插即用登录设置。您可以使用各种登录过程来验证用户,例如 Facebook、Twitter 或用户的电子邮件地址。这个过程将在以下主题中详细解释。
注册
您可以使用以下代码来展示允许用户使用电子邮件 ID 和密码注册的一种方式:
- (void)signUp {
PFUser *user = [PFUser user];
user.username = @"demo name";
user.password = @"demo pass";
user.email = @"demo@example.com";
// other fields can be set just like with PFObject
[user setObject:@"user display name" forKey:@"displayName"];
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
// Successfully signed up
} else {
NSString *errorString = [[error userInfo] objectForKey:@"error"];
// Show the errorString somewhere and let the user try again.
}
}];
}
signUpInBackgroundWithBlock: 方法会异步(推荐)地在您的 Parse 应用程序中创建一个新用户。此方法封装了基本验证,例如电子邮件验证以及用户名和电子邮件 ID 的唯一性。Parse 将所有密码以哈希形式保存在云端。您还可以将用户的电子邮件 ID 作为其用户名保存。
Parse 上有各种 signUp 方法的版本。您可以从 API 文档中的方法列表中访问所有这些方法。
验证用户
Parse 还允许您验证用户的电子邮件 ID。在验证电子邮件 ID 后,PFUser类将添加emailVerified键。该字段包含 false 值,并且一旦用户通过其 ID 验证了他们的电子邮件地址,它就会更新为 true。
登录
在注册后,用户将登录其账户以访问应用程序。为了允许用户登录应用程序,您可以使用PFUser类的logInWithUsernameInBackground:password:类方法:
[PFUser logInWithUsernameInBackground:@"demoName" password:@"demopass"
block:^(PFUser *user, NSError *error) {
if (user) {
// Successful login.
} else {
// The login failed. Check error to see why.
}
}];
登录成功后,您可以访问PFUser对象,它将包含所有用户相关数据。
保存用户数据
对于应用程序的用户来说,每次打开应用程序时都进行登录是繁琐的。为了避免这种情况,您需要使用缓存来保存用户数据。保存用户详情将允许用户每次无需通过登录过程即可使用应用程序。PFUser类提供自动缓存策略,缓存对象保存在currentUser对象中:
PFUser *currentUser = [PFUser currentUser];
if (currentUser) {
// do stuff with the user
} else {
// show the signup or login screen
}
您可以通过在PFUser类上调用logOut方法来重置当前用户。
[PFUser logOut];
创建匿名用户
您可以允许用户使用您的应用程序,而无需强迫他们登录您的应用程序。可以创建一个没有用户名和密码的匿名用户,具有所有PFUser的功能。您可以通过提供 Facebook、Twitter 或电子邮件 ID 登录,随时将匿名用户转换为普通用户。
以下代码演示了创建匿名用户的方法:
[PFAnonymousUtils logInWithBlock:^(PFUser *user, NSError *error) {
if (error) {
NSLog(@"Anonymous login failed.");
} else {
NSLog(@"Anonymous user logged in.");
}
}];
匿名用户的所有数据在注销后将不可访问。然而,如果用户注册为普通用户,所有数据都将持久化,并且可以被用户访问。
Parse 提供了检查用户是否已链接的方法。以下代码将演示检查用户是否为匿名的方法:
if ([PFAnonymousUtils isLinkedWithUser:[PFUser currentUser]]) {
[self enableSignUpButton];
} else {
[self enableLogOutButton];
}
重置密码
当用户开始登录您的应用程序时,他们经常会忘记用户名和密码。在这种情况下,您需要为用户提供一种重置密码的方式。Parse 为您提供了requestPasswordResetForEmailInBackground:方法,以安全地重置用户密码。
以下代码将说明重置密码功能的使用:
[PFUser requestPasswordResetForEmailInBackground:@"demo@example.com"];
重置密码链接将被发送到提供的电子邮件 ID,用户可以从那里输入应用程序的新密码。
编写用户查询
以下代码将演示如何在PFUser类上编写查询:
PFQuery *query = [PFUser query];
[query whereKey:@"gender" equalTo:@"female"]; // find all the women
NSArray *girls = [query findObjects];
上述代码将为您提供所有输出中的女性用户。
提供安全性
在保存用户数据时,确保所有用户数据都应该是安全的。用户数据和相关信息非常重要,它们应该安全地保存。Parse 允许您在云上安全地保存您的数据。
更新用户对象
PFUser 类已经得到了保护。在 PFUser 中保存的数据只能由该用户更新。此外,您不能在 PFUser 类上调用任何 save 或 delete 方法。唯一可以更新 PFUser 类上数据的 logIn 和 signUp 方法。这确保了只有用户可以更新他们在云上的数据:
The following code will illustrate the security of the user on cloud.
PFUser *user = [PFUser logInWithUsername:@"my_username" password:@"my_password"];
user.username = "my_new_username"; // attempt to change username
[user save]; // This succeeds, since the user was authenticated on the device
// Get the user from a non-authenticated method
PFQuery *query = [PFUser query];
PFUser *userAgain = (PFUser *)[query getObjectWithId:user.objectId];
userAgain.username = "another_username";
// This will throw an exception, since the PFUser is not authenticated
[userAgain save];
要获取用户对象的认证副本,请使用 currentUser 方法,您可以通过使用 isAuthenticated 布尔属性来检查用户的真实性。
保护对象
您也可以将 PFUser 的安全模型用于其他对象。您可以在 Parse 上保存的对象上指定读/写权限。您可以允许特定的用户组读取对象,以及其他可以修改对象的用户组。为了安全起见,每个对象都提供了一个访问控制列表,由 PFACL 类实现。
以下代码将展示如何设置当前用户在 Parse 上的对象隐私:
PFObject *privateNote = [PFObject objectWithClassName:@"Note"];
[privateNote setObject:@"This note is private!" forKey:@"content"];
privateNote.ACL = [PFACL ACLWithUser:[PFUser currentUser]];
[privateNote saveInBackground];
ACLWithUser 方法可以帮助您生成仅限制当前用户访问对象的 ACL。在调用 save 方法后,ACL 将被更新。受保护的数据将可供登录在设备上的用户访问。
您可以更新您的 Parse 对象的权限,针对不同的用户。以下两个方法将帮助您更新 Parse 对象的权限:
-
要提供读访问权限,请使用
setReadAccess:forUser: -
要提供写访问权限,请使用
setWriteAccess:forUser:
以下代码将展示如何设置读/写访问权限:
PFObject *groupMessage = [PFObject objectWithClassName:@"Message"];
PFACL *groupACL = [PFACL ACL];
// userList is an NSArray with the users we are sending this message to.
for (PFUser *user in userList) {
[groupACL setReadAccess:YES forUser:user];
[groupACL setWriteAccess:YES forUser:user];
}
groupMessage.ACL = groupACL;
[groupMessage saveInBackground];
您也可以使用 setPublicReadAccess: 和 setPublicWriteAccess: 方法将读写权限授予所有用户。
要为应用程序中的所有对象提供一个通用的访问控制列表,您可以更改默认的 ACL:
PFACL *defaultACL = [PFACL ACL];
[defaultACL setPublicReadAccess:YES];
[PFACL setDefaultACL:defaultACL withAccessForCurrentUser:YES];
在前面的代码中,我们正在更新默认的 ACL,以提供对所有对象的公共访问权限。
分配角色
随着您的应用程序的增长,访问控制列表(ACL)变得不足。您需要更加约束,并采取更好的方法来控制用户访问。Parse 为您提供角色来解决此问题。角色允许您创建用户逻辑组。角色是一个包含用户和其他角色的对象。更新任何角色的权限将更新该角色中所有相关用户的权限。
例如,您可以将应用程序用户分为管理员、标准用户和访客角色。更改任何角色的权限将更新属于该组的所有用户的权限。提供角色功能的是 PFRole 类。这个类从 PFObject 继承,因此在 PFRole 中也可以访问所有方法。
PFRole
PFRole 类除了 PFObject 中的属性外,还提供了以下附加属性:
-
name: 此字段将存储角色的名称,例如管理员或标准用户。 -
users:此字段将存储PFUser对象作为关系。用户将继承其关联角色的所有权限。 -
roles:此字段将保存PFRole对象的实际权限。
保护角色对象
PFRole 类使用与其他所有 Parse 对象相同的 ACL 安全机制。
以下代码将展示创建新的 PFRole 对象的方法:
// By specifying no write privileges for the ACL, we can ensure the role cannot be altered.
PFACL *roleACL = [PFACL ACL];
[roleACL setPublicReadAccess:YES];
PFRole *role = [PFRole roleWithName:@"Administrator" acl:roleACL];
[role saveInBackground];
之前的代码将创建一个新的 role 对象,现在它可以与 User 对象关联。用户访问控制将根据 role 对象进行更新。
以下代码将关联 User 与 role 对象,并使用角色提供的新 ACL 更新用户 ACL:
PFRole *role = [PFRole roleWithName:roleName acl:roleACL];
for (PFUser *user in usersToAddToRole) {
[role.users addObject:user];
}
for (PFRole *childRole in rolesToAddToRole) {
[role.roles addObject:childRole];
}
[role saveInBackground];
在分配 ACLs 到您的角色时要格外小心,因为它们将直接影响用户的可访问性。
保护其他对象
现在您已经学会了如何为用户提供角色。在用户对象的 PFACL 类中,您可以指定哪些用户和角色应该被授予对哪些对象的读取或写入访问权限。
以下代码将展示在 PFObject 中使用角色的用法:
PFObject *wallPost = [PFObject objectWithClassName:@"WallPost"];
PFACL *postACL = [PFACL ACL];
[postACL setWriteAccess:YES forRole:@"Administrator"];
wallPost.ACL = postACL;
[wallPost saveInBackground];
实现角色层次结构
您可以将一个角色作为另一个角色的子集提供。这意味着一个角色可以包含另一个角色。您可以在角色之间创建父子关系。这种关系的结果是,授予父角色的任何权限将隐式地授予其所有子角色。
例如,您可以说 Administrator 角色包含在 Moderator 角色中授予的所有权限。以下代码将解释角色层次结构的实现:
PFRole *administrators = /* Your "Administrators" role */;
PFRole *moderators = /* Your "Moderators" role */;
[moderators.roles addObject:administrators];
[moderators saveInBackground];
之前的代码将在 Administrator 和 Moderator 角色之间添加一个关系。在这里,Administrator 角色是 Moderator 的子角色。
摘要
在本章中,我们探讨了使用 Parse 的应用程序的用户和角色管理。
我们从探索具有其属性和登录过程的 PFUser 开始。
然后,我们学习了用户和其他对象的安全性。
最后,我们探讨了 PFRole 以扩展用户权限和安全,同时分组用户。
在下一章中,我们将学习如何在应用中使用 Parse 集成社交媒体(Facebook、Twitter)。
第七章。社交媒体集成
这些天,社交媒体集成是所有移动应用程序都包含的常见功能。一些应用程序允许用户将数据更新分享到他们的社交媒体资料,如 Facebook 和 Twitter。
现在,使用电子邮件 ID 和密码的传统登录过程正变得越来越繁琐。因此,为了解决这些问题,许多应用程序使用 Facebook 和 Twitter 作为其基础登录。用户可以使用他们的 Facebook 或 Twitter 账户进行注册。这甚至简化了登录过程的步骤,并为用户提供了一键注册和登录。考虑到这些因素,Parse 提供了即插即用的方法来集成社交媒体,无论是用于分享还是用于登录过程。在本章中,你将详细了解使用 Parse 进行社交媒体集成。
集成 Facebook
将 Facebook 集成到你的 Parse 应用程序中很容易。Facebook SDK 可以与 Parse SDK 一起使用。你可以将你的 PFUser 与 Facebook 用户身份链接。你也可以使用 Facebook 进行注册过程。Parse 允许你从 Facebook 验证用户并将用户关联到你的 PFUser 类。
在本节中,你将学习如何从你的 Parse 应用程序中集成 Facebook。
前提条件
要开始将 Facebook 与 Parse 集成,你需要设置以下事项:
-
在 Facebook 开发者门户([
developers.facebook.com/](https://developers.facebook.com/))上注册并创建一个 Facebook 应用程序([https://developers.facebook.com/docs/ios/getting-started/](https://developers.facebook.com/docs/ios/getting-started/)),如果你还没有创建的话。 -
Facebook 提供了创建应用程序的逐步指南(https://developers.facebook.com/apps)并将其与 Facebook SDK 集成。
设置应用程序
要开始使用 Facebook,你需要在应用程序中配置以下内容:
-
从左侧面板中选择你的项目文件,点击 目标 并导航到 信息 选项卡。
-
在 自定义 iOS 目标属性 部分中,添加一个新键,将 Facebook App Id 作为字符串值,并提供你的 Facebook 应用 ID 作为值。
-
现在添加另一个带有 URL 类型 的键名。展开键名,最后你会看到 URL 标识符。将 URL 标识符 替换为 URL 方案 并填写
fbYour_App_Id(例如,Fb1234567890)。
在你的应用程序中更新 Facebook 键后,执行以下步骤以设置代码:
-
在你的
application:didFinishLaunchingWithOptions:方法中,添加以下代码行以初始化 Facebook,就在你初始化 Parse SDK 的代码下方:[PFFacebookUtils initializeFacebook]; -
在你的
AppDelegate文件中添加以下方法以处理 Facebook SDK 的单点登录功能:- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [PFFacebookUtils handleOpenURL:url]; } - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { return [PFFacebookUtils handleOpenURL:url]; }
在 Facebook 上注册
使用 Parse SDK,您可以允许用户使用 Facebook 注册。Parse 还将为您创建和保存用户对象。以下方法将允许您使用 Facebook 登录:
[PFFacebookUtils logInWithPermissions:permissions block:^(PFUser *user, NSError *error) {
if (!user) {
NSLog(@"Uh oh. The user cancelled the Facebook login.");
} else if (user.isNew) {
NSLog(@"User signed up and logged in through Facebook!");
} else {
NSLog(@"User logged in through Facebook!");
}
}];
在之前的代码中,permissions是一个字符串数组,指定了您的应用程序想要从 Facebook 访问哪些数据。
以下代码将展示您如何使用 Facebook 登录或注册:
- (IBAction)loginButtonTouchHandler:(id)sender {
// The permissions requested from the user
NSArray *permissionsArray = @[ @"user_about_me", @"user_relationships", @"user_birthday", @"user_location"];
// Login PFUser using Facebook
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
[_activityIndicator stopAnimating]; // Hide loading indicator
if (!user) {
if (!error) {
NSLog(@"Uh oh. The user cancelled the Facebook login.");
} else {
NSLog(@"Uh oh. An error occurred: %@", error);
}
} else if (user.isNew) {
NSLog(@"User with facebook signed up and logged in!");
[self.navigationController pushViewController:[[UserDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:YES];
} else {
NSLog(@"User with facebook logged in!");
[self.navigationController pushViewController:[[UserDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:YES];
}
}];
}
在按钮动作中,您可以添加之前的代码以使用 Facebook 登录或注册。在之前的代码中,我们使用了permissionsArray。此数组将包含表示从 Facebook 需要获取的用户数据的字符串。
执行前面的函数后,将发生以下步骤:
-
应用程序将被重定向到 Facebook 或提示获取访问 Facebook 数据的权限。
-
当用户通过 Facebook 进行身份验证时,您的应用程序将使用我们在上一节中配置的
handleOpenUrl方法接收回调。 -
如果用户是新的,Parse SDK 将获取数据并将其保存到
PFUser类中,并将数据保存在 Parse 上。 -
您的代码块将使用用户调用。
由于 Parse 已被 Facebook,Inc.收购,Parse 提供了与 Facebook 的非常深入的集成,这也预示着未来 Parse 将实现更加深入和广泛的集成。
在 Facebook 上发布
要在用户墙上发布一些更新,您需要从用户那里获取发布权限。以下代码将获取用户的发布权限:
[PFFacebookUtils reauthorizeUser:[PFUser currentUser]
withPermissions:@["publish_stream"]
audience:PF_FBSessionDefaultAudienceFriends
block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
//Your app now has publishing permissions for the user
}
}];
请求用户数据
用户登录后,您可以使用 Graph API 开始从 Facebook 获取用户数据。以下代码将展示如何从 Facebook 获取用户数据:
- (void)fetchUserData {
// ...
// Create request for user's Facebook data
FBRequest *request = [FBRequest requestForMe];
// Send request to Facebook
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
// result is a dictionary with the user's Facebook data
NSDictionary *userData = (NSDictionary *)result;
NSString *facebookID = userData[@"id"];
NSString *name = userData[@"name"];
NSString *location = userData[@"location"][@"name"];
NSString *gender = userData[@"gender"];
NSString *birthday = userData[@"birthday"];
NSString *relationship = userData[@"relationship_status"];
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large&return_ssl_resources=1", facebookID]];
// Now add the data to the UI elements
// ...
}
}];
}
result字典将包含所有请求的数据。您可以通过键查询来获取数据。
链接用户
如果是匿名登录或使用电子邮件 ID 和密码注册,可以将现有的PFUser与 Facebook 账户关联。以下代码将展示如何将现有用户链接到 Facebook:
if (![PFFacebookUtils isLinkedWithUser:user]) {
[PFFacebookUtils linkUser:user permissions:nil block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(@"User logged in with Facebook!");
}
}];
}
登录成功后,PFUser类将更新为包含 Facebook 信息。
以下代码将取消将 Facebook 账户从当前用户中链接:
[PFFacebookUtils unlinkUserInBackground:user block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(@"The user is no longer associated with their Facebook account.");
}
}];
登出
要从应用程序中登出当前用户,您只需在PFUser类上调用logOut方法。以下代码将从应用程序中登出用户:
- (void)logoutButtonAction:(id)sender {
[PFUser logOut]; // Log out
// Return to login page
}
保持用户登录
每次启动应用程序时用户都需要登录,这显得有些繁琐。这是因为 Facebook 会话信息在启动之间不会被保存。Parse 通过在PFUser类的currentUser对象中内部保存会话信息,使我们能够轻松地实现这一点。
在启动视图控制器中,您可以检查当前会话。如果存在,您可以绕过登录。以下代码将展示如何绕过登录:
- (void)viewDidLoad {
...
if ([PFUser currentUser] && // Check if a user is cached
[PFFacebookUtils isLinkedWithUser:[PFUser currentUser]]) // Check if user is linked to Facebook
{
// Push the next view controller without animation
[self.navigationController pushViewController:
[[UserDetailsViewController alloc]
initWithStyle:UITableViewStyleGrouped]
animated:NO];
}
}
集成 Twitter
就像 Facebook 集成一样,Parse 提供了一个简单的方法来集成 Twitter 认证。只需几行代码,您就能使用 Twitter 登录并保存数据到 Parse Cloud。
设置 Twitter
您需要执行以下步骤来使用 Parse 设置 Twitter 认证:
-
如果您还没有在 Twitter 上创建应用程序,您需要在 Twitter 开发者门户上创建您的应用程序(
dev.twitter.com/apps)。 -
在 Parse 应用程序的设置页面上添加您的 Twitter 应用程序的消费者密钥。
-
为 Twitter 应用程序输入一个有效的回调 URL。这个值对于您控制这个回调 URL 非常重要,这样信息就不会在传输过程中被劫持。因此,以这种方式,这个值被 Twitter 用于认证目的。
-
在您的
application:didFinishLaunchingWithOptions:方法中添加以下代码,就在初始化 Parse 的代码下方:[PFTwitterUtils initializeWithConsumerKey:@"YOUR CONSUMER KEY" consumerSecret:@"YOUR CONSUMER SECRET"];
在您的应用程序中设置 Twitter 的所有步骤如下。现在,我们可以允许用户使用 Twitter 进行登录。
注册和登录
Parse 提供了PFTwitterUtils类,允许您的用户使用 Twitter 进行登录。以下代码将允许用户使用 Twitter 进行注册:
[PFTwitterUtils logInWithBlock:^(PFUser *user, NSError *error) {
if (!user) {
NSLog(@"Uh oh. The user cancelled the Twitter login.");
return;
} else if (user.isNew) {
NSLog(@"User signed up and logged in with Twitter!");
} else {
NSLog(@"User logged in with Twitter!");
}
}];
在执行前面的代码后,应该发生以下情况:
-
用户将通过弹窗或登录对话框被提示允许访问 Twitter 认证。
-
在授权应用程序后,应用程序将收到一个回调。
-
Parse 将在登录成功后接收数据,并将其保存到云上的
PFUser类。
链接用户
如果是匿名登录或使用电子邮件 ID 和密码注册,您可以将现有的 PFUser 与 Twitter 账户关联起来。以下代码将展示如何将现有用户链接到 Twitter:
if (![PFTwitterUtils isLinkedWithUser:user]) {
[PFTwitterUtils linkUser:user block:^(BOOL succeeded, NSError *error) {
if ([PFTwitterUtils isLinkedWithUser:user]) {
NSLog(@"User logged in with Twitter!");
}
}];
}
登录成功后,PFUser类将更新为包含 Twitter 信息。
以下代码将解除 Twitter 账户与当前用户的链接:
[PFTwitterUtils unlinkUserInBackground:user block:^(BOOL succeeded, NSError *error) {
if (!error && succeeded) {
NSLog(@"The user is no longer associated with their Twitter account.");
}
}];
概述
在这一章中,我们探索了 Facebook 和 Twitter SDK 与 Parse SDK 的集成。
我们首先通过在 Facebook 开发者门户上设置应用程序来探索 Facebook 集成,然后看到了登录设置。
然后,我们学习了如何在 Facebook 上发布帖子,以及如何链接 Facebook 用户。
接下来,我们学习了如何在应用程序中集成。
最后,我们在应用程序中探索了 Twitter 集成和登录。
在下一章中,我们将学习云函数及其在我们应用程序中的使用。
第八章:使用云函数
在应用程序开发中,并非所有过程都可以在客户端执行。因此,建议在云端执行此类过程。Parse 允许用户开发移动应用程序,绕过服务器端编码和管理。在开发复杂应用程序时,用户希望某些业务逻辑不在客户端执行。因此,对于此类应用程序,Parse 提供了 Parse Cloud,您可以在其中部署自定义逻辑,该逻辑可以通过您的应用程序访问。
云代码必须用 JavaScript 语言编写。区别在于执行位置;云代码将在 Parse Cloud 上执行,而不是在移动设备上执行。一旦云代码更新,它将立即对所有移动环境可用。这些功能可以帮助您轻松地即时更改应用程序的行为。在本章中,我们将学习云代码的实现及其在移动端的用法。
云代码
在开始使用云代码之前,您需要设置 Parse 命令行工具。此工具将帮助您管理和部署代码到云上。
安装命令行工具
要在 Mac/Linux 环境中安装 Parse 命令行工具,您需要在终端窗口中执行以下命令:
curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash
上述代码行将在/usr/local/bin/parse目录下安装一个名为parse的工具。要卸载,只需删除文件即可,因为它没有安装任何垃圾文件。
设置云代码
要设置云代码,创建一个您想要保存云代码的目录。建议将云代码保存在您的项目工作区中。
命令parse new为您创建一个新的目录,并提示选择您要为它创建云代码的应用程序:
$ parse new MyCloudCode
Email: demo@gmail.com
Password:
1:DemoApp
Select an App: 1
$ cd MyCloudCode
您需要使用 Parse 账户的电子邮件地址和密码进行登录。如果使用OAuth登录,您需要从 Parse 设置中设置新密码以设置云代码。在成功执行前面的命令后,将为您创建以下文件结构:
-config/
global.json
-cloud/
main.js
-public/
index.html
以下是对前面提到的文件的说明:
-
config目录中的 JSON 文件不应被编辑,它是供 Parse 使用的 -
cloud目录是您存储云代码的地方 -
初始时,
config文件夹将包含main.js,它包含您的云函数 -
public目录将包含用于在 Parse 上托管静态数据内容的静态数据
创建云函数
要在云上创建函数,您需要将函数添加到您的cloud/main.js文件中。以下是一个cloud函数的示例:
Parse.Cloud.define("demoCloudCode", function(request, response) {
response.success("Cloud integration is easy!");
});
在您的终端中运行以下命令以在 Parse Cloud 上部署代码:
$ parse deploy
一旦函数部署到云端,你可以在 Parse 应用的云代码部分查看云代码。部署的代码应该在那里发布。现在,是时候在移动端执行云代码了。Parse 为你提供了callFunctionInBackground:withParameters:block:方法,用于从移动端执行云代码。
您必须使用以下代码来执行云代码:
[PFCloud callFunctionInBackground:@"demoCloudCode"
withParameters:@{}
block:^(NSString *result, NSError *error) {
if (!error) {
NSLog(@"%@", result);
// result is @"Cloud integration is easy!"
}
}];
因此,在本节中,我们探讨了如何编写和执行云代码。
托管一个网站
在 Parse 上托管一个网站非常简单。你public目录中的所有文件都将托管在your-custom-subdomain.parseapp.com。以下代码将展示如何在 Parse Cloud 上托管你的网站。打开终端并执行以下代码:
$ echo "Hello World" > public/index.html
$ parse deploy
上一行代码将在您的index.html文件中添加Hello World,并且使用deploy命令,您的代码将被部署到 Parse 云端。
要更新您的子域名,请导航到您应用设置的Web Hosting部分。在ParseApp 名称字段中提供唯一的名称,您的所有网站文件都将可在your-custom-subdomain.parseapp.com上访问。
理解复杂函数
云端代码对于减少客户端的计算量非常重要。比如说,如果你的应用程序需要计算与 MBA 课程相关的学生的平均成绩。Marks对象将如下所示:
{
"student": "John Melon",
"marks": 5,
"course": "MBA"
}
在这种情况下,你不应该获取一个长的数据列表并在客户端进行过滤。你可以添加一个用于过滤结果并提供结果数据的 Parse 代码。以下 Parse 云端代码将帮助你进行数据过滤:
Parse.Cloud.define("averageMarks", function(request, response) {
var query = new Parse.Query("Marks");
query.equalTo("course", request.params.course);
query.find({
success: function(results) {
var sum = 0;
for (var i = 0; i < results.length; ++i) {
sum += results[i].get("marks");
}
response.success(sum / results.length);
},
error: function() {
response.error("course lookup failed");
}
});
});
以下是需要注意的关于前一个 Cloud 函数的要点:
-
定义一个
cloud函数,并对Marks对象进行查询 -
根据在
params query中提供的course名称过滤Marks对象params.query.equalTo("course", request.params.course); -
然后获取过滤后的数据,在完成计算后返回平均值
现在要在客户端使用云代码获取数据,你需要使用以下代码:
[PFCloud callFunctionInBackground:@"averageMarks"
withParameters:@{@"course": @"MBA"}
block:^(NSNumber *marks, NSError *error) {
if (!error) {
// marks is 45
}
}];
之前的代码将调用名为 averageMarks 的 cloud 函数,并将 MBA 课程名称作为参数。在云代码成功执行后,你将获得 marks 作为响应,该响应将包含与 MBA 课程相关联的学生的平均分数。
使用云存储的代码
在多平台移动应用开发的情况下,云代码可以节省在客户端编写的大量代码。假设您有一个用于应用开发的 iOS、Android 或 Windows 环境,那么在这种情况下,您可以通过在云上部署验证代码来避免在所有环境中进行编码验证。云为您提供beforeSave方法。以下代码将展示其用法:
Parse.Cloud.beforeSave("averageMarks", function(request, response) {
if (request.object.get("marks") < 1) {
response.error("you cannot give less than one mark");
} else if (request.object.get("marks") > 50) {
response.error("you cannot give more than five marks");
} else {
response.success();
}
});
通过这种方法,Parse 允许你在对象在云上保存之前添加自定义逻辑。在这个函数中,你可以在对象上添加验证代码。
同样,Parse 也为你提供了afterSave方法,这将帮助你提供自定义逻辑,该逻辑将在对象保存后执行:
Parse.Cloud.afterSave("averageMarks", function(request) {
query = new Parse.Query("Marks");
query.get(request.object.get("course").course, {
success: function(course) {
post.increment("marks");
post.save();
},
error: function(error) {
console.error("Got an error " + error.code + " : " + error.message);
}
});
});
之前的代码将在对象在云上保存后执行。这种方法允许你添加自定义逻辑,该逻辑将在对象保存后执行。
在删除代码中使用云
就像保存一样,你可以在删除任何对象之前和之后添加自定义代码。Parse 为你提供了beforeDelete和afterDelete方法,这将帮助你在这些事件上添加自定义逻辑。
以下代码将展示beforeDelete方法的用法。同样,你也可以使用afterDelete方法:
Parse.Cloud.beforeDelete("Marks", function(request) {
query = new Parse.Query("Marks");
query.equalTo("course", request.object.course);
query.count({
success: function(count) {
if (count > 0) {
response.error("Can't delete.");
} else {
response.success();
}
},
error: function(error) {
response.error("Error " + error.code + " : " + error.message + " when getting photo count.");
}
});
});
摘要
在这一章中,我们探讨了 Parse 云代码及其各种实现,以简化移动端的应用开发。
我们首先探索了命令行工具的安装过程,然后我们看到了在 Parse 上设置云代码。
我们还学习了编写云函数及其在客户端的用法。
然后,我们学习了如何在云上发布我们的网站。
最后,我们探讨了使用云的复杂场景和实现。
在下一章中,我们将学习关于 Parse 的错误处理和安全。
第九章。错误处理和安全
在处理云代码时,用户可能在查询中发送无效的参数。为了处理云代码上的此类条件,我们必须向这些查询发送错误代码。错误可以有多种形式,例如超时、ACL 列表、未定义的操作等等。
在本章中,我们将涵盖:
-
为了增强应用程序的稳定性以处理此类错误并相应地响应的错误处理
-
提高数据安全性的措施
-
导出你的 Parse 数据的方法
错误处理
在使用 Parse 的时候,你可能会遇到两种类型的错误。第一种是逻辑错误,这可能是由于你使用 SDK 的方式引起的。这种类型的错误通常会引发一个名为 NSException 的异常。
假设在使用登录的情况下,理想的登录流程是在调用 PFUser 类上的 signUp 方法之前提供用户名和密码。所以,根据以下代码,你绕过了用户名和密码,直接调用 signUp:
PFUser *user = [PFUser user];
[user signUp];
在这种情况下,操作将抛出 NSInternalInconsistencyException 异常,因为 signUp 方法被调用时没有用户名和密码属性。
与 Parse 云交互时,也可能会发生错误。错误可能是由于与 Parse 云的连接,也可能是由于任何请求的操作。让我们看看另一个例子:
- (void)fetchCourse {
PFQuery *query = [PFQuery queryWithClassName:@"Course"];
[query getObjectInBackgroundWithId:@"invalidObjectId"
target:self
selector:@selector(callbackForFetch:error:)];
}
在前面的代码中,我们正在尝试获取在 Parse 云上不可用的对象。在这种情况下,Parse 将以字典格式抛出错误,其中错误代码可以通过 error.code 读取,消息可以通过 error.userinfo 读取。
以下代码演示了前一个方法调用的 callback 方法:
- (void)callbackForFetch:(PFObject *)result error:(NSError *)error {
if (result) {
NSLog(@"Success!");
} else {
if ([error code] == kPFErrorObjectNotFound) {
NSLog(@"Object not available!");
} else if (error) {
NSLog(@"Error: %@", [[error userInfo] objectForKey:@"error"]);
}
}
}
在前面的代码中,你会注意到我们正在使用 kPFErrorObjectNotFound 来检查错误代码。Parse 提供了各种预定义的常量用于错误处理。你可以在 PFConstants 头文件中找到所有预定义的错误代码。
查询失败可能有其他几个可能的原因,例如设备无法连接到 Parse 云服务器。在这种情况下,你将收到一个包含错误描述的错误消息。为此,callback 方法将类似于以下代码片段:
- (void)callbackForFetch:(PFObject *)result error:(NSError *)error {
if (result) {
NSLog(@"Success!");
} else {
if ([error code] == kPFErrorObjectNotFound) {
NSLog(@"Object not available!");
// Now also check for connection errors:
} else if ([error code] == kPFErrorConnectionFailed) {
NSLog(@"Connection failed with Parse Cloud!");
} else if (error) {
NSLog(@"Error: %@", [[error userInfo] objectForKey:@"error"]);
}
}
}
当你向云代码发送 NSNumber 作为响应时,其布尔值用于检查操作是否成功。例如,你调用了一个 cloud 函数来获取学生的平均分数。在这种情况下,callback 方法应该类似于以下方法:
- (void)callbackForAverageMarks:(NSNumber *)result error:(NSError *)error {
if ([result boolValue]) {
NSLog(@"Success!");
} else {
if ([error code] == kPFErrorConnectionFailed) {
NSLog(@"Connection failed with Cloud!");
} else if (error) {
NSLog(@"Error: %@", [[error userInfo] objectForKey:@"error"]);
}
}
}
在前一个方法中,如果结果布尔值为 true,则操作成功,否则操作失败并带有错误。错误处理对所有前台任务都是相同的。
所有通信的请求超时时间为 10 秒,因此同步调用不会长时间挂起。同步和异步任务都使用相同的连接超时。
Parse 为您提供错误代码列表及其解释。您可以在www.parse.com/docs/ios/api/Classes/PFConstants.html上查看此列表。
保护您的应用程序数据
在软件行业中,尽可能保护您的应用程序数据被认为是最佳实践。为了保护您的数据,您可以使用 Parse 提供的访问控制列表,根据用户为对象提供安全性。建议在应用程序启动时自动启用匿名用户。还建议仅在所需的对象上提供公共可写性。此类设置将保护您的应用程序数据免受未经授权的访问。以下代码将说明在您的应用程序中使用安全性的用法:
[PFUser enableAutomaticUser];
PFACL *defaultACL = [PFACL ACL];
// Optionally enable public read access while disabling public write access.
// [defaultACL setPublicReadAccess:YES];
[PFACL setDefaultACL:defaultACL withAccessForCurrentUser:YES];
根据您在第六章“用户和角色”中学习到的内容,建议为所有对象提供 ACL。
您可以通过在您的应用设置页面上自定义以下设置来进一步增强您的应用程序的安全性:
-
如果您不使用它们,请禁用所有登录机制。例如,如果您的应用程序仅允许用户使用 Facebook 登录,那么请从设置中禁用所有其他登录方法。
-
在您的 Parse 应用程序设置页面上提供您的 Facebook应用 ID和 Twitter消费者密钥信息,以启用服务器端验证用户登录尝试。
导出数据
Parse 还允许您导出您的数据库。要导出数据,请导航到应用程序的设置页面并点击导出数据按钮。一旦您点击导出数据按钮,Parse 将压缩所有数据并将其导出到您的电子邮件 ID,该 ID 用于登录。导出的数据是 JSON 格式。ZIP 文件将包含所有类对象在一个单独的文件中。以下是从 Parse 导出的Class对象的示例数据:
{ "results": [
{
"name": "buy groceries",
"createdAt": "2013-07-03T11:04:05.062Z",
"updatedAt": "2013-07-05T10:15:08.909Z",
"objectId": "fYQ5ZfsM9m"
},
{
"name": "team planning",
"createdAt": "2013-08-15T07:22:51.976Z",
"updatedAt": "2013-08-15T07:22:51.976Z",
"objectId": "sBsAfr2Fm5"
}
] }
摘要
在本章中,我们探讨了 Parse 云代码及其各种实现,以简化移动端的应用程序开发。
我们首先探索了在项目中处理错误类型和方式。
然后,我们学习了如何增强应用程序的安全性。
最后,我们看到了如何从 Parse 导出我们的数据。













浙公网安备 33010602011771号