前言,数据的增删改查很简单请到https://developer.stackmob.com/ios-sdk/developer-guide#Datastore查看。
1、只进行一次请求,创建多个关联的表
NSDictionary *classW = [NSDictionary dictionaryWithObjectsAndKeys:@"wangluo101",@"className" ,nil]; NSDictionary *classJ = [NSDictionary dictionaryWithObjectsAndKeys:@"jisuanji101",@"className" ,nil]; NSDictionary *DepartmentW = [NSDictionary dictionaryWithObjectsAndKeys:@"网络系",@"departmentName",classW,@"class", nil]; NSDictionary *DepartmentJ = [NSDictionary dictionaryWithObjectsAndKeys:@"计算机系",@"departmentName",classJ,@"class", nil]; NSArray *departments = [NSArray arrayWithObjects:DepartmentW,DepartmentJ, nil]; NSDictionary *academy = [NSDictionary dictionaryWithObjectsAndKeys:@"信息学院",@"academyName",departments,@"department", nil]; SMRequestOptions *option = [SMRequestOptions options]; [option associateKey:@"department" withSchema:@"department"]; [option associateKey:@"department.class" withSchema:@"class"]; [[[SMClient defaultClient]dataStore] createObject:academy inSchema:@"academy" options:option onSuccess:^(NSDictionary *object, NSString *schema_) { NSLog(@"success"); } onFailure:^(NSError *error, NSDictionary *object, NSString *schema_) { NSLog(@"error"); }];
//以上代码执行后,数据库会生成三个表,一个是academy,一个是department,一个是class。并且三个表之间的关联关系已经指定好。也就是academy下的department字段保存了两个department纪录的id,department表下的class字段保存了一个class表的一条纪录的id。这段代码既有一对多又有一对一,注意写法的区别。
2、创建用户并登陆
SMRequestOptions *option = [SMRequestOptions optionsWithHTTPS]; NSDictionary *user = [NSDictionary dictionaryWithObjectsAndKeys:[[alertView textFieldAtIndex:0] text],@"username",@"123321",@"password", nil]; [[[SMClient defaultClient]dataStore] createObject:user inSchema:@"user" options:option onSuccess:^(NSDictionary *object, NSString *schema_) { NSLog(@"注册成功"); [[SMClient defaultClient]loginWithUsername:[[alertView textFieldAtIndex:0] text] password:@"123321" onSuccess:^(NSDictionary *result) { NSLog(@"登陆成功"); } onFailure:^(NSError *error) { NSLog(@"登陆失败"); }]; } onFailure:^(NSError *error, NSDictionary *object, NSString *schema_) { NSLog(@"error"); }];
//需要注意的是,user表必须是Created as a User Object = YES,否则登陆失败。
3、检索当前登陆用户的信息
[[SMClient defaultClient] getLoggedInUserOnSuccess:^(NSDictionary *result) { NSLog(@"%@",result); } onFailure:^(NSError *error) { }];
这个主要是从服务端的角度来看用户是登陆了。
4、用户登陆与被踢
当一个用户登陆成功的时候,response会包含两个token,一个是access token,一个是refresh token。access toke用来对后续的request进行身份的识别,但是access token有过期的机制,当access token过期的时候,我们可能需要进行输入用户名、密码等等。但是,有了refresh token之后,一切都变的简单了。我们只需要用refresh token进行一次新的request。如果request成功返回了,我们会得到一个新的access token 和新的refresh token。
需要我们注意的是,一个用户先在一台设备上进行了登陆,然后又在第二台设备上进行了登陆。那么,第一台设备上的refresh token就invalid。这意味着,当第一台设备尝试进行刷新登陆的时候,服务器会拒绝这个请求,并给出refresh token invalid的提示。如果你想尽可能的无缝处理这个事件,你需要做的事是设置刷新失败的block.
[[SMClient defaultClient] setTokenRefreshFailureBlock:^(NSError *error, SMFailureBlock originalFailureBlock){ // You are given the refresh error as well as the failure callback from the original request if you choose to call it. // We recommend logging in again by presenting a temporary login screen to the user, then picking up where they left off in the UI. // Optionally, you can store the username and password in the app's keychain and use it to refresh the login without any user interaction. }];
5、退出登陆
[[SMClient defaultClient]logoutOnSuccess:^(NSDictionary *result) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"下线成功" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; } onFailure:^(NSError *error) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"下线失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; }];
6、重置密码
重置密码需要以下几个步骤
1)页面上有一个忘记密码的按钮
2)当用户点击了忘记密码,那么提示用户输入忘记密码的用户名,实际上,这个用户名可以是邮箱,这样会减少user表的字段。
3)当用户名输入完成之后,点击提交,那么在你填的邮箱里会收到一个临时密码。
5)用临时密码登陆。如果你会捕捉到错误,这个error的code是-101,表示这是个临时密码,你应该跳转到修改密码页面。
6)跳转到修改密码页面之后,设置新密码点击登陆后,新密码设置成功。
以上几步用到的函数
登陆
[[SMClient defaultClient] loginWithUsername:usernamestr password:passwordstr onSuccess:^(NSDictionary *result) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"登陆成功" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; } onFailure:^(NSError *error) { if (error.code == -101)//用的是临时密码 { NewPasswordController *newPassword = [[NewPasswordController alloc]initWithUser:usernamestr tempPassword:passwordstr]; [self.navigationController pushViewController:newPassword animated:YES]; return ; } UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"登陆失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; }];
获得临时密码
[[SMClient defaultClient] sendForgotPaswordEmailForUser:passwordModel.email onSuccess:^(NSDictionary *result) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"新密码已经发送到邮箱" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; } onFailure:^(NSError *error) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"修改密码失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; }];
修改密码
[[SMClient defaultClient]loginWithUsername:user temporaryPassword:temp settingNewPassword:passwordModel.password onSuccess:^(NSDictionary *result) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"新密码登陆成功" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; } onFailure:^(NSError *error) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"新密码登陆失败" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; }];
7、密保
StackMob提供了锁定访问的功能,你可以禁止增删改查。甚至你可以在用户的级别上严格的控制这些权利,例如:
选择最右第一项之后,只允许登陆用户进行增进行create。
8、地理位置服务
StackMob提供了一个类,叫GeoPoint,在这个类de一个实例当中你可以保存经纬度坐标。在服务端有这个数据类型,也就是说在表中你可以存这种数据类型。
开始定位
[[[SMLocationManager sharedInstance] locationManager] startUpdatingLocation];
设置定位成功回调的block
[SMGeoPoint getGeoPointForCurrentLocationOnSuccess:^(SMGeoPoint *geoPoint) { }];
SMGeoPoint从NSDictnory继承。里面包含了经纬度。
在回调当中我们可以做如下的处理
[[[SMLocationManager sharedInstance]locationManager] stopUpdatingLocation]; CLLocation *location = [[CLLocation alloc]initWithLatitude:[[geoPoint objectForKey:@"lat"] doubleValue] longitude:[[geoPoint objectForKey:@"lon"] doubleValue]]; CLGeocoder *encoder = [[CLGeocoder alloc]init]; [encoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) { if(placemarks.count <= 0) return ; CLPlacemark *placemark = [placemarks objectAtIndex:0]; NSDictionary *addressdictionary = placemark.addressDictionary; NSString *address =[addressdictionary objectForKey:(NSString*)kABPersonAddressStreetKey]; address = address==nil?@"":address; NSString *state = [addressdictionary objectForKey:(NSString*)kABPersonAddressStateKey]; state = state == nil?@"":state; NSString *city = [addressdictionary objectForKey:(NSString*)kABPersonAddressCityKey]; city = city == nil?@"":city; NSString *locationStr = [NSString stringWithFormat:@"你在%@省%@市%@街",state,city,address]; UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"定位成功" message:locationStr delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show];
也就是把经纬度反编码为实际的地理位置。
9、查看附近的人
- (void)checkNear :(id)sender { [[[SMLocationManager sharedInstance]locationManager] startUpdatingLocation]; [SMGeoPoint getGeoPointForCurrentLocationOnSuccess:^(SMGeoPoint *geoPoint) { SMQuery *query = [[SMQuery alloc]initWithSchema:@"location"]; [query where:@"coordinate" isWithin:5 milesOfGeoPoint:geoPoint]; [[[SMClient defaultClient]dataStore] performQuery:query onSuccess:^(NSArray *results) { for (NSDictionary *point in results) { NSLog(@"%@",[point objectForKey:@"username"]); } } onFailure:^(NSError *error) { }]; } onFailure:^(NSError *error) { }]; }
10、文件的存储
- (void)setHeadAction: (id)sender { NSBundle *bundle = [NSBundle bundleForClass:[self class]]; NSString* pathToImageFile = [bundle pathForResource:@"head" ofType:@"png"]; NSData *data = [[NSData alloc]initWithContentsOfFile:pathToImageFile]; NSString *dataStr = [SMBinaryDataConversion stringForBinaryData:data name:@"[email protected]" contentType:@"image/png"]; [[SMClient defaultClient] getLoggedInUserOnSuccess:^(NSDictionary *result) { NSDictionary *imageDic = [NSDictionary dictionaryWithObjectsAndKeys:dataStr,@"picture", [result objectForKey:@"username"],@"username",nil]; SMQuery *query = [[SMQuery alloc]initWithSchema:@"location"]; [query where:@"username" isEqualTo:[result objectForKey:@"username"]]; [[[SMClient defaultClient]dataStore] performQuery:query onSuccess:^(NSArray *results) { [[[SMClient defaultClient]dataStore] updateObjectWithId:[results[0] objectForKey:@"location_id"] inSchema:@"location" update:imageDic onSuccess:^(NSDictionary *object, NSString *schema) { NSLog(@"头像设置成功"); } onFailure:^(NSError *error, NSDictionary *object, NSString *schema) { NSLog(@"头像设置失败"); }]; } onFailure:^(NSError *error) { }]; } onFailure:^(NSError *error) { }]; }
把NSData转换为NSString,然后在保存到服务器上去。有一个我问题需要注意的是picture的字段必须是Binary,这时候StackMob会提醒你去注册s3,这个是要花钱的。
9、检查网络状态
SMNetworkStatus status = [[SMClient defaultClient].networkMonitor currentNetworkStatus]; if(status == SMNetworkStatusReachable) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"网络畅通" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; } if(status == SMNetworkStatusNotReachable) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"无网络" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; } if(status == SMNetworkStatusUnknown) { UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"未知的网络" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; [alert show]; }
10、进行推送。
stackmob 推送是收费的,每月399$,我没有进行测试,测试不起,这里不再详细讲解。