CocoaPods 是一个强大的第三方库管理工具,可以最大限度地简化安装过程。因此,我们推荐您使用此方法安装 SDK。
首先,安装 CocoaPods,具体安装方法请参考:《CocoaPods 安装和使用教程》
CocoaPods 安装完成后,在项目根目录下创建一个名为 Podfile
的文件(无扩展名),并添加以下内容:
pod 'JavaBaasSDK'
然后,在终端中执行 pod install
以安装。安装完成后,运行 pod search JavaBaasSDK
以确认 SDK 已安装到本地库。
JBObject
是基础对象和数据类型,其本质是一个 JSON 字符串。可以像使用 NSDictionary
一样,给 JBObject
赋值取值。
由于与 NSDictionary
的相似性,每个 JBObject
对象都包含若干属性值对,也就是键值对(key-value)。属性值可以直接设定,也可以随时添加新属性值。
例如,如果我们需要一个音乐播放器类型的 app,那么我们可以创建一个表名为 Single
(单曲)的 JBObejct
对象,并包含以下属性:
singer : "张三";
songName : "张三的歌";
length : 241;
需要注意的是,以下所列出的为系统保留字段,由系统自动生成或更新,既不可作为属性名使用,也无需开发者进行指定。
_id createdAt
acl updatedAt
每个 JBObject
都必须有一个与其对应的表名称,用以区分不同类型的数据。例如,单曲这个对象,可以将表名取为 Single
。
那么,现在我们可以创建一个名为“张三的单曲”的单曲对象(Single):
JBObject *single = [JBObject objectWithClassName:@"Single"];
[single setObject:@"张三" forKey:@"singer"];
[single setObject:@"张三的单曲" forKey:@"songName"];
[single setObject:@(241) forKey:@"length"];
JavaBaas SDK 同时提供了数据查询、保存、更新等的同步和异步方法。
例如,我们要保存上面创建好的“张三的单曲”的单曲对象:
//同步方法-保存数据
NSError *error = nil;
[single save:&error];
//异步方法-保存数据
[single saveInBackgroundWithBlock:^(BOOL succeeded, NSError) {
if (error) {
//single保存失败
} else {
//single保存成功
}
}];
在 iOS 或 OS X 中,大多数代码都是在主线程中运行的。但是,当程序在主线程中访问网络时,会出现卡顿崩溃,并且在通常情况下,我们需要在一些操作完成后立即运行后面的代码,因此同步方法不应放在主线程中运行。
如果已知 objectId,可以使用 JBQuery 查询到与之相对应的唯一 JBObject。例如:...
JBQuery *query = [JBQuery queryWithClassName:@"Single"];
//同步方法-检索单对象
NSError *error = nil;
JBObject *single = [getObjectOfClass:@"Single" objectId:@"ac31c72291854630824dbe94bf269748" error:&error];
//异步方法-检索单对象
[query getObjectInbackgroudId:@"ac31c72291854630824dbe94bf269748" block:^(JBObject *object, NSError *error) {
if (error) {
//返回错误,查询失败
} else {
//查询成功
}
}];
如果我们需要发布或上传一首单曲(single),则需要调用 save
方法,数据才能被真正保存。
例如,将创建好的“张三的单曲”保存到服务器:
//同步方法-保存数据
NSError *error = nil;
[single save:&error];
//异步方法-保存数据
[single saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error){
if (error) {
//返回错误,保存失败
} else {
//保存成功
}
}];
运行上面示例代码后,要确认保存是否生效,可以到云端的数据管理页面查看数据存储情况。如果已经保存成功,那么在 Single 的数据表中应该显示出以下记录:...
objectId:"ac31c72291854630824dbe94bf269748", singer: "张三", songName:"张三的单曲", length:251,
createdAt:"2016-01-03 11:13:39", updatedAt:"2016-01-03 11:13:39"
更新对象相对简单,只需要更新属性,然后再保存即可。例如:...
//假设我们现在要对上面"张三的单曲"这一单曲对象更新一些属性
[single setObject:@(3000) forKey:@"downloadCount"];
[single saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
//更新属性失败
} else {
//更新属性成功
}
}];
需要注意的是,更新对象都是针对单个对象的操作,必须获得对象的 objectId
才可以更新对象。服务器根据是否有 objectId
判断对象是新增还是更新。
此外,对于一些特殊情况,例如 Number 类型的字段,我们需要记录某个音频的打开次数(readCount),然而对于一个热门音频,可能会有很多并发打开音频操作,如果每次操作我们都是通过请求获取该音频当前的 readCount,然后加 1,再通过请求写回后台,那么这很容易造成数据冲突和冲突覆盖,最终导致结果不准确。对于这种场景,在更新对象的操作中,我们提供了一些原子操作:...
[single incrementKey:@"readCount"];
[single saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
// code
}];
除了 incrementKey
这个原子操作外,我们还提供了其他几种原子操作:...
方法 | 描述 |
---|---|
removeKey:(NSString *)key | 删除字段 |
addArray:(NSArray *)objects forKey:(NSString *)key | 向 NSArray 类型字段添加值 |
addUniqueArray:(NSArray *)objects forKey:(NSString *)key | 向 NSArray 类型字段添加与之前不重复的值 |
removeArray:(NSArray *)objects forKey:(NSString *)key | 从 NSArray 类型字段中删除值 |
incrementKey:(NSString *)key byAmount:(NSNumber *)amount; | NSNumber 类型字段原子增加或减少 |
multiply:(NSString *)key byAmount:(NSNumber *)amount | NSNumber 类型字段原子倍数增加 |
删除一个 JBObject
对象:...
//同步方法-删除文件
NSError *error = nil;
[single delete:&error];
如果需要在删除后进行操作,可以使用 deleteInBackgroundWithBlock:
//异步方法-删除文件
[single deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
//返回失败,删除对象失败
} else {
//删除成功
}
}];
对象可以相互关联。我们可以将一个 JBObject
的实例 a,作为另一个 JBObject
的实例 b 的属性值保存。
例如,一首单曲属于一张专辑,创建一张专辑信息并对应一首单曲,那么,这首单曲就是实例 a,而它所属的专辑就是实例 b。专辑可以作为单曲的属性保存。因此可以这样写:...
//创建专辑、名称
JBObject *myAlbum = [JBObject objectWithoutDataWithClassName:@"Album" objectId: 1a5b907a272c47fd977708ebf6bfe958];
[myAlbum setObject:@"王五的专辑" forKey:@"title"];
//创建单曲、歌名
JBObject *mySingle = [JBObject objectWithClassName:@"Single"];
[mySingle setObject:@"王五的单曲" forKey:@"songName"];
//为专辑、单曲建立一对一关系
[mySingle setObject:myAlbum forKey:@"album"];
//同时保存myAlbum、mySingle
//同步方法-保存对象
NSError *error = nil;
[mySingle save:&error];
//异步方法-保存对象
[mySingle saveInBackgroudWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
//返回错误,保存失败
} else {
//保存成功
}
}];
默认情况下,在获取到一个 JBObject
对象实例时,与之相关联的 JBObject
对象的属性值是获取不到的。这些对象除了 objectId
之外,其他属性值都是空的。例如,我们获取到一个单曲对象,而它关联的专辑对象的属性值,除了 objectId
,其他的名称、发布时间等都是空的。要获得全部这些属性数据,请使用 include 获取关联对象的所有属性:...
JBQuery *query = [JBQuery queryWithClassName:"Single"];
[query includeKey:@"album"];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索多对象
[query findObjectsInBackgroundWithBlock:^(NSArray objects, NSError *error) {
if (error) {
} else {
//返回的Single对象所关联的Album对象的属性值都已获取到
}
}];
目前,我们使用过的数据类型有 NSString
、NSDate
、NSNumber
、NSArray
、NSDictionary
和 JBObject
。
SDK 中的 JBQuery
类提供了多种检索方法,以满足诸如单对象查询、多对象查询、缓存查询等多种需求。
单对象查询:getObjectInBackgroundWithId:block:
,只能查询单个对象实例。
多对象查询:findObjectsInBackgroundWithBlock:
,在一般情况下,在这之前需要创建一个 JBQuery
对象,并设置相应的查询条件,之后 block 会返回 JBObject 组成的 NSArray,满足条件。
例如,需要查找指定歌手(singer)的所有单曲,可以使用 whereKey:equalTo:
设置查询条件。
//假定已知歌手名为"张三"的Singer对象"zhangsan"
JBQuery *query = [JBQuery queryWithClassName:@"Single"];
[query whereKey:@"singer" equalTo:zhangsan];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索单对象
[query findObjectsInBackgroudWithBlock:^(NSArray *objects, NSError) {
if (error) {
//查询失败,输出错误信息
} else {
//查询成功,返回singer为"张三"的所有Single对象
}
}];
向 JBQuery
的查询添加约束条件有几种方法。
whereKey:equalTo:
、whereKey:notEqualTo:
用于过滤对象,使用对应的键和值。
//查询歌手不是张三的单曲
[query whereKey:@"singer" notEqualTo:@"张三"];
一个查询可以设置多个约束条件,只有满足所有约束条件的对象才被返回,这相当于使用 and 类型的查询条件。
//查询歌手不是张三并且单曲时长超过180s的单曲
[query whereKey:@"singer" notEqualTo:@"张三"];
[query whereKey:@"length" greaterThan:@(180)];
limit
:限制返回结果的数量。返回数量默认是 100,limit 的范围是 1 到 1000。
query.limit = 20; //最多返回20条结果
skip
:跳过初始结果,对于分页非常有用。
query.skip = 20; //跳过前20条查询结果
addAscendingOrder
、addDescendingOrder
:用于添加排序键。
//按照播放次数升序排列
[query addAscendingOrder:@"playTimes"];
//按照播放次数降序排列
[query addDescendingOrder:@"playTimes"];
查询中“比较”, whereKey:lessThan
(小于)、whereKey:lessThanOrEqualTO
(小于等于)、whereKey:greaterThan
(大于)、whereKey:greaterThanOrEqualTo
(大于等于):
//下载次数 < 100
[query whereKey:@"downloadCount" lessThan:@(100)];
//下载次数 <= 100
[query whereKey:@"downloadCount" lessThanOrEqualTo:@(100)]
//下载次数 > 100
[query whereKey:@"downloadCount" greaterThan:@(100)];
//下载次数 >= 100
[query whereKey:@"downloadCount" greaterThanOrEqualTo:@(100)];
查询中的“存在”,whereKeyExist
(存在)、whereDoesNotExist
(不存在):
//检索所有存在MV的单曲对象
JBQuery *query = [JBQuery queryWithClassName:@"Single"];
[query whereKeyExist:@"mv"];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索多对象
[query findObjectsInBackgroudWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
//返回错误,查询失败
} else {
//返回所有存在MV的单曲对象
}
}];
//检索所有不存在MV的单曲对象
JBQury *query = [JBQuery queryWithClassName:@"Single"];
[query whereKeyDoesNotExist:@"mv"];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索多对象
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
//返回错误,查询失败
} else {
//返回又说不含MV的单曲对象
}
}];
当属性值为数组时,可以使用whereKey:containedIn
//假定已知歌手名为"张三"的zhangsan(Singer)对象和歌手名为"李四"的lisi(Singer)对象,检索出歌手为张三或李四的单曲对象
[query whereKey:@"singer" containedIn:@[zhangsan, lisi]];
//查询所有名字name中包含“张”的歌手
JBQuery *query = [JBQuery queryWithClassName:@"Singer"];
[query whereKey:@"name" containsString:@"张"];
查询关系数据的方法有多种。可以使用whereKey:equalTo
,就像使用其他数据类型一样。
例如,每个单曲Single
的singer
字段都有一个Singer
歌手对象,那么找出指定歌手的单曲:
// 假定已经获取到歌手名为“张三”的singer这个JBObject对象
JBQuery *query = [JBQuery queryWithClassName:"Single"];
[query whereKey:@"singer" equalTo:singer];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索多对象
[query findObjctsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// 返回的objects数组就是singer字段为张三所有Single对象
}];
如果要做嵌套查询,应使用whereKey:matchesQuery
,举例来说,检索专辑销量超过50000的所有单曲对象:
JBQuery *innerQuery = [JBQuery queryWithClassName:@"Album"];
[innerQuery whereKey:@"saleCount" greaterThan:@(50000)];
;
JBQuery *query = [JBQuery queryWithClassName:@"Single"];
[query whereKey:@"album" matchesQuery:innerQuery];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索多对象
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
//objects包含了所有专辑销量超过50000的单曲对象
}];
如果要用一个对象中某一键值,去匹配另一个查询结果对象中一个键值,来得到最终结果,可以使用whereKey:matchesKey:matchesClass:inQuery
,例如,检索当前用户所关注歌手的所有单曲对象:
//获取当前用户关注的歌手列表
JBQuery *followQuery = [JBQuery queryWithClassName:@"FollowSinger"];
[followQuery whereKey:@"user" equalTo:[JBUser currentUser]];
JBQuery *singleQuery = [JBQuery queryWithClassName:@"Single"];
[singleQuery whereKey:@"singer" matchesKey:@"followSinger" matchesClass:@"Singer" inQuery:followQuery];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索多对象
[singleQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error){
if (error) {
//返回错误,查询失败
} else {
//objects返回的就是当前用户所关注歌手的所有单曲对象
}
}];
而如果是复合查询的话,可以使用orQueryWithSubqueries
。
例如,检索出下载次数很多或者下载次数很少的单曲:
JBQuery *lotsOfDownload = [JBQuery queryWithClassName:@"Single"];
[lotsOfDownload whereKey:@"downloadCount" greaterThan:@(1000)];
JBQuery *fewDownload = [JBQuery queryWithClassName:@"Single"];
[fewQuery whereKey:@"downloadCount" lessThan:@(10)];
JBQuery *query = [JBQuery orQueryWithSubqueries:[NSArray arrayWithObjects:lotsOfDownload, fewDownload, nil]];
//同步方法-检索多对象
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法-检索多对象
[query findObjectsInBackgroundWithBlock:^(NSArray *objectsm NSError *error) {
if (error) {
//返回错误,查询失败
} else {
//返回所有下载次数大于1000或小于10的单曲对象
}
}];
注意:在复合查询的子查询中,不能使用非过滤性的约束(如limit、skip、includeKey)等。
如果设备离线或者失去网络连接时,打开应用,希望数据也能显示出来,所以通常来说,将请求结果缓存到磁盘是最为简单有效的方法。
而默认的查询不会查询缓存数据,需要通过JBQuery的cachePolicy属性来设置。
例如,网络无法连接时,
JBQuery *query = [JBQuery queryWithClassName:@"Single"];
query.cachePolicy = JBCachePolicyCacheOnly;
//同步方法
NSError *error = nil;
NSArray *objects = (NSArray *)[query findObjects:&error];
//异步方法
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
//查询缓存失败,没有返回数据
} else {
//查询缓存成功,成功获取缓存数据
}
}];
只需要获取查询结果的数量,而不需要获取具体的对象时,可以使用countObject
和countObjectsInBackgroundWithBlock
方法。
例如,如果我们想知道某张专辑有多少首单曲的时候:
JBQuery *query = [JBQuery queryWithClassName:@"Album"];
[query whereKey:@"album" equalTo:@"李四的专辑"];
//同步方法-计数查询
NSError *error = nil;
NSInteger count = [query countObjects:&error];
//异步方法-计数查询
[query countObjectsInBackgroundWithBlock:^(NSInteger number, NSError *error) {
if (error) {
//返回错误
} else {
//返回查询结果的数量
}
}];
ACL(Access Control List)是数据安全管理办法,设置了访问修改权限,更好地保证用户数据安全,因此每一个表都有一个ACL列。
拥有读权限的用户才能获取一个对象的数据,拥有写权限的用户才能更改或删除一个对象。
默认情况下,每一个对象都是可读可写的。但当设置了ACL之后,默认的ACL就会被覆盖。
JBACL *acl = [JBACL ACL];
[acl setPublicReadAccess:YES]; //设置全部用户均可读
[acl setPublicWriteAccess:YES]; //设置全部用户均可写
大部分时候,不同用户针对同一对象访问权限是不同的,那么就需要指定用户访问权限:
JBACL *acl = [JBACL ACL];
[acl setPublicReadAccess:YES];
[acl setWriteAccess:YES forUser:[JBUser currentUser]];
JBFile
是继承自JBObject
的子类。是用来处理文件管理所需功能的专门类。
JBFile
允许应用将文件存储到服务端,支持图片、视频等常见的文件类型,以及其他任何二进制数据。
NSData *data = [NSdata dataWithContentsOfURL:http://7xnus0.com2.z0.glb.qiniucdn.com/5645b2a574242e39eee89829/c25b2241637b49c8bc021a60abb5f23e];
JBFile *file = [JBFile fileWithName:@"video.mp4" data:data];
如果需要存储的文件是来自网上,可以使用JBFile提供的方法fileWithURL:
,所以上面的代码也可以这么写:
JBFile *file = [JBFile fileWithURL:http://7xnus0.com2.z0.glb.qiniucdn.com/5645b2a574242e39eee89829/c25b2241637b49c8bc021a60abb5f23e];
使用saveInBackgroundWithBlock:progressBlock:
可以获取到JBFile
的上传进度。例如:
[file saveInBackgroundWithBlock:^(id object, NSError *error) {
//上传成功或失败的逻辑处理
} progressBlock:^(float percentDone) {
//更新进度数据
}];
JBUser
与JBFile
一样都是JBObject
的子类,是用来处理用户账户管理所需功能的专门用户类。它不仅继承了JBObject
所有的方法,具备与JBObject
相同的功能,还添加了一些特定的与用户账户相关的功能。
JBUser
除了继承自JBObject
的属性外,还有一些特有属性:
username
: 用户的用户名(必需且唯一)password
: 用户的密码(必需)phone
: 用户用来注册的手机号码(可选)email
: 用户用来注册的电子邮件地址(可选)auth
: 用户授权第三方登录(可选)大部分程序都需要用户注册,例如:
JBUser *myUser = [JBUser user];
[user setObject:@"张三" forKey:@"username"];
[user setObject:@"123456" forKey:@"password"];
//同步方法-用户注册
NSError *error = nil;
[user signUp:&error];
//异步方法-用户注册
[user singUpInBackgroundWithBlock:^(id object, NSError *error) {
if (error) {
//注册失败
} else {
//注册成功
}
}];
##7.4 登录(修改)
让已经成功注册的用户登录到自己的账户,可以调用JBUser
类中登录相关的同步/异步方法,并根据用户选择登录方式的不同调用不同的方法。例如:
//同步方法-用户名、密码登录
NSError *error = nil;
[JBUser logInWithUsername:@"张三" password:@"123456" error:&error];
//异步方法-用户名、密码登录
[JBUser logInWithUsernameInBackground:@"张三" password:@"123456" block:^(id object, NSError *error) {
if (error) {
//登录失败
} else {
//登录成功
}
}];
//第三方授权登录(目前支持的需要传入从第三方平台获取到的accessToken和uid, 并传入登录平台,如QQ、微信微博等)
//同步方法-第三方授权登录
NSError *error = nil;
[JBUser logInWithAuthData:{@"accessToken" : @"8343726DA09DB9830CC32486A4856E0A", @"uid" : @"638C29277C7538E555DFF0EF40BBADCD"} authType:JBPlatformQQ error:&error];
//异步方法-第三方授权登录
[JBUser logInWithAuthDataInBackground:{@"accessToken" : @"8343726DA09DB9830CC32486A4856E0A", @"uid" : @"638C29277C7538E555DFF0EF40BBADCD"} authType: JBPlatformQQ block:^(id object, NSError) {
if (error) {
//登录失败
} else {
//登录成功
}
}];
目前支持的第三方平台有:
用户,是应用程序的核心。如果每次打开应用程序都要登录,会直接影响用户体验。为避免这种情况,可以使用缓存的currentUser
对象。当用户成功注册或者第一次成功登录后,就将当前用户对象缓存在本地中,既方便下次调用,也给用户以最好的应用体验。
JBUser *current = [JBUser currentUser];
if (currentUser != nil) {
//本地缓存用户对象不为空,当前用户已登录
} else {
//本地缓存用户对象为空,当前用户未登录
}
清除缓存的用户对象:
[JBUser logout]; //清除本地缓存用户对象
JBUser *currentUser = [JBUser currentUser]; //现在currentUser是nil了
当用户使用非第三方授权登录而是用户名密码或手机密码登录时,就会有更改密码的需求,我们也提供了相应的方法来满足用户的这一需求:
[JBUser logInWithUsernameInBackground:@"张三" password:@"123456" block:^(id object, NSError *error) {
if (error) {
//登录失败
} else {
//登录成功
}
}];
//同步方法-重置密码
NSError *error = nil;
[[JBUser currentUser] updatePassword:@"123456" newPassword:@"000000" error:&error];
//异步方法-重置密码
[[JBUser currentUser] updatePassword:@"123456" newPassword:@"000000" block:^(id object, NSError *error) {
if (error) {
//返回错误,更改密码失败,可能是用户尚未登录、原密码错误或用户不存在等原因
} else {
//更改密码成功
}
}];
SessionToken
是JBUser
的一个非常特殊的属性,是
JBUser
的内建字段。当用户注册成功后,自动生成且唯一。
当用户更改或重置密码后,SessionToken
也会被重置。
SessionToken
的作用主要有两个方面
JBInstallation
同样也是一个继承自JBObject
的子类,是用来处理设备管理所需功能的专门类。
deviceToken
: 设备的唯一标识符deviceType
: 对于iOS设备来说,type就是"iOS"可以使用JBCloud类的静态方法调用云代码中定义的函数:
[JBCloud callFunctionInBackgroud:@"functionName" withParameters:@{...} block:^(id object, NSError *error {
//返回结果,业务逻辑
})];
functionName是云代码中函数的名称, parameters是传入函数的参数,block对象作为调用结果的回调传入。