之前写过一片AVOS一些常用的使用, 比如用户注册, 登录, 数据的查询, 图片的加载以及初始化AVOS使用解析, 慢慢的在自己玩一些东西的过程中发现AVOS确实是一个不错的后端支撑, 简称Baas, 移动应用后端服务。
所以在慢慢的开发过程中, 使用RAC以及AVOS快速的建立一套登录体系, 以及数据的Fetch, 云代码的自定义与调用, AVObject与本地Model的相互转换也慢慢的熟悉了起来。
代码浅显易懂, 所以不做过多的解释了。
- Model - AVObject的相互转换
- 为了后期把服务切到自己的服务器上来, 所以初期我们不要直接使用AVObject进行操作, 而是自己写Model, 与AVObject进行相互转换。这里的Model是继承JSONModel来写的, 这里JSON与实体的相互转换大家还可以使用MJExtension、Mantle等。实体的与JSON的转换大大提高了我们开发的效率, 也便于项目的扩展, 所以推荐大家使用。 团队人数足够的时候, 可以直接使用RestKit进行开发, 可以快速的部署并与服务器进行数据通信, 支持CoreData(插嘴:CoreData苹果自己都不用, 大家还是多看看SQL吧,所以我们依旧在用FMDB)。
BaseModel.h
#import "JSONModel.h"
@interface BaseModel : JSONModel
/// Adapter model -> AVObject
- (AVObject *)toAVObject;
/// Initialized with AVObject
- (instancetype)initWithAVObject: (AVObject *)object;
/// Initialized with avClassName
- (instancetype)initWithClassName: (NSString *)avClassName;
//! @abstract AV ClassName
@prop_strong(NSString *, avClassName);
@end
BaseModel.m
#import "BaseModel.h"
@implementation BaseModel
+ (BOOL)propertyIsOptional:(NSString *)propertyName {
return YES;
}
/// Adapter model -> AVObject
- (AVObject *)toAVObject {
if (!self.avClassName) {
return nil;
}
AVObject * object = [AVObject objectWithClassName:self.avClassName];
for (NSString * attributeName in [self classAttributesArray]) {
id value = [self valueForKey:attributeName];
if (value) {
[object setObject:value forKey:attributeName];
}
}
NSLog(@"%@", object);
return object;
}
/// Initialized with AVObject
- (instancetype)initWithAVObject: (AVObject *)object {
self = [super init];
if (self) {
for (NSString * attributeName in [self classAttributesArray]) {
id value = [object objectForKey:attributeName];
if (value) {
[self setValue:value forKey:attributeName];
}
}
}
return self;
}
- (instancetype)initWithClassName:(NSString *)avClassName {
self = [super init];
if (self) {
self.avClassName = avClassName;
}
return self;
}
@end
在上边用到了 [self classAttributesArray]
下边给出这个方法的内容, 运行时获取类的属性名称, 这个方法之前在Hack融云输入框的文章中有用到, 并很详细。
NSObject(Runtime)
- (NSMutableArray *)classAttributesArray {
NSString *className = NSStringFromClass([self class]);
const char * cClassName = [className UTF8String];
id classM = objc_getClass(cClassName);
unsigned int outCount, i;
objc_property_t * properties = class_copyPropertyList(classM, &outCount);
NSMutableArray * array = [NSMutableArray array];
for (i = 0; i < outCount; i++) {
objc_property_t property = properties[i];
NSString * attributeName = [NSString stringWithUTF8String:property_getName(property)];
[array addObject:attributeName];
}
return array;
}
- 自己写操作合集, 而不是直接调用.
- 建议在使用AVOS的时候自己封装一下操作, 而不是在使用的地方直接调用, 因为项目后期肯定是要迁移到自己的Server上的, 所以这样封装后, 在准备迁移的时候更改方法体即可。这里只给出简单的登录注册以及验证码验证等操作.
AVOSOperate.h
@interface AVOSOperate : NSObject
/// Load AVOS
+ (void)LoadAVOS;
/// Login
+ (void)LoginWithNumber: (NSString *)number passWord: (NSString *)passWord;
/// Regist
+ (void)RegisterWithNumber: (NSString *)number passWord: (NSString *)passWord nickName: (NSString *)nickName;
/// Verify
+ (void)VerifyNumber: (NSString *)number;
@end
AVOSOperate.m
#import "AVOSOperate.h"
@implementation AVOSOperate
+ (void)LoadAVOS {
[AVOSCloud setApplicationId:@"0ypwnkwb9bl5vq3y9uplcy7luxow37sbjfwg31ni2swplksn"
clientKey:@"gswb6dos30r6wojks6ifmxxs1r59d2pwnp5wryrwem0dztmr"];
[AVOSCloud useAVCloudCN];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[AVOSCloud registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound) categories:nil];
#pragma clang diagnostic pop
}
+ (void)LoginWithNumber: (NSString *)number passWord: (NSString *)passWord {
NSLog(@"%@ - %@", number, passWord);
[AVUser logInWithMobilePhoneNumberInBackground:number password:passWord block:^(AVUser *user, NSError *error) {
if (!error) {
[LoginManager sharedInstance].isLogin = YES;
[[LoginManager sharedInstance] Record:number pass:passWord];
} else {
[DLOperation sharedInstance].shake = YES;
[SVProgressHUD showErrorWithStatus:[error localizedDescription]];
}
}];
}
+ (void)RegisterWithNumber: (NSString *)number passWord: (NSString *)passWord nickName: (NSString *)nickName {
AVUser * user = [AVUser user];
user.username = number;
user.password = passWord;
user.mobilePhoneNumber = number;
[user setObject:nickName forKey:@"nickName"];
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[LoginManager sharedInstance].isLogin = YES;
[[LoginManager sharedInstance] Record:number pass:passWord];
[LoginManager sharedInstance].isSendVerifyCode = YES;
} else {
[DLOperation sharedInstance].shake = YES;
[SVProgressHUD showErrorWithStatus:[error localizedDescription]];
}
}];
}
+ (void)VerifyNumber: (NSString *)number {
[AVUser verifyMobilePhone:number withBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
[LoginManager sharedInstance].VerifySucceed = YES;
} else {
[DLOperation sharedInstance].shake = YES;
[SVProgressHUD showErrorWithStatus:[error localizedDescription]];
}
}];
}
@end
在上边代码中用到的类似这样的值[LoginManager sharedInstance].isLogin = YES;
是为了RAC的方便。在界面中我对操作结果进行监听。
LoginViewController.m
- (void)LoginAction {
// Login Success, Set Login Yet
[self.view endEditing:YES];
if ([self AuthText:_userNameText.text] && [self AuthText:_passWordText.text]) {
[[LoginManager sharedInstance] Login:_userNameText.text pass:_passWordText.text];
@weakify(self);
[SVProgressHUD showWithStatus:@"正在登录..." maskType:SVProgressHUDMaskTypeClear];
[[RACObserve([LoginManager sharedInstance],isLogin )filter:^BOOL(NSNumber *value) {
return value.floatValue == TRUE;
}]subscribeNext:^(NSNumber * x) {
@strongify(self)
[self.navigationController presentViewController:(UIViewController *)[DLOperation sharedInstance].tabBar animated:YES completion:nil];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
[SVProgressHUD showSuccessWithStatus:@"登录成功"];
}];
} else {
[self.view shakeView];
}
}
RAC
的使用方法这里不做讲解, 下篇文章会写RAC的使用以及小技巧以及iOS9.0的一些新特性并给出Demo, 像3DTouch之类, GitPage我已经把代码Fork到手, 慢慢的做详细的讲解, iOS9.0的适配相信大家已经看过不少, 这里不多嘴舌了。并且教大家制作快速的监听器, 其实之前的文章DLObserver使用的就是一些这其中的小技巧。学无止境, 多看大牛。
另外, 在验证AVOS速度不错的时候, 也使用了一些小代码片段, 有一段是计算这个方法走了多久的、 不过仅同步有效, 异步需要自己写一下。
double a = CFAbsoluteTimeGetCurrent();
block();
double b = CFAbsoluteTimeGetCurrent();
unsigned int m = ((b-a) * 1000.0f); // convert from seconds to milliseconds
}
3.CLLocation自己封装的使用
- 位置的使用相信大家可以在百度或者Google上边招到很多的代码, 所以这里不做过多的解释, 直接把代码扔出来大家看吧。 里边包括了位置编解码、计算路线等操作。
DLLocationManager.h
//
// DLLocationManager.h
//
//
// Created by XueYulun on 15/7/10.
//
//
///----------------------------------
/// @name 位置管理
///----------------------------------
/*
1. NSLocationAlwaysUsageDescription
2. NSLocationWhenInUseUsageDescription
*/
//! @name 参考文献: http://blog.csdn.net/weisubao/article/details/43205229 大头针的自定义, 划线.
typedef NS_ENUM(NSInteger, DLLocationReques) {
DLLocationReques_Always = 1UL << 0,
DLLocationReques_WhenUse = 2UL << 1
};
#import
#import
#import
typedef void(^didUpdateLocation)(CLLocation * location, NSError * error);
typedef void(^didGeocodeLocation)(NSDictionary * addressInfoDict, NSError * error);
typedef void(^didGeocodeAddress)(CLPlacemark * placeMark, NSError * error);
@interface DLLocationManager : NSObject
@prop_strong(CLLocationManager *, locationManager); // 定位器
@prop_strong(CLGeocoder *, geocoder); // 解码器
@prop_strong(CLPlacemark *, placemark); // 最近的一次解码结果
@singleton(DLLocationManager);
- (void)UpdateLocationWithAccuracy: (CLLocationAccuracy)accuracy Update:(DLLocationReques)requestType CompleteBlock: (didUpdateLocation)locationBlock;
// CLLocation对象 - 具体定位信息字典
- (void)GeocodeWithLocation: (CLLocation *)Location CompleteBlock: (didGeocodeLocation)geocodeBlock;
// NSString位置 - CLPlacemark, 可用于添加大头针, 计算线路, 划线等. 一次只能对一个位置进行编码。
- (void)GeocodeAddressString: (NSString *)Place CompleteBlock: (didGeocodeAddress)geocodeBlock;
- (void)GeocodeAddressString:(NSString *)Place WithRegion:(CLRegion *)region CompleteBlock:(didGeocodeAddress)geocodeBlock;
// 2点之间的线路, 并且划线
- (void)MapView: (MKMapView *)mapView AddLineFrom: (CLPlacemark *)From to: (CLPlacemark *)To;
@end
DLLocationManager.m
//
// DLLocationManager.m
//
//
// Created by XueYulun on 15/7/10.
//
//
#import "DLLocationManager.h"
@interface DLLocationManager ()
@prop_copy(didUpdateLocation, locationBlock);
@end
@implementation DLLocationManager
@def_singleton(DLLocationManager);
- (void)UpdateLocationWithAccuracy: (CLLocationAccuracy)accuracy Update:(DLLocationReques)requestType CompleteBlock: (didUpdateLocation)locationBlock {
self.locationBlock = locationBlock;
self.locationManager.desiredAccuracy = accuracy;
// iOS 8.0
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
BOOL hasAlwaysKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] != nil;
BOOL hasWhenInUseKey = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil;
requestType == DLLocationReques_Always ? [self.locationManager requestAlwaysAuthorization] : [self.locationManager requestWhenInUseAuthorization];
if (!hasAlwaysKey && !hasWhenInUseKey) {
NSAssert(hasAlwaysKey || hasWhenInUseKey, @"在iOS 8.0 之后使用定位, 首先在INFO.plist文件中加入下面2项中的一项 NSLocationWhenInUseUsageDescription, NSLocationAlwaysUsageDescription.");
}
}
[self.locationManager startUpdatingLocation];
}
- (void)GeocodeWithLocation: (CLLocation *)Location CompleteBlock: (didGeocodeLocation)geocodeBlock {
@weakify(self);
[self.geocoder reverseGeocodeLocation:Location completionHandler:^(NSArray *placemarks, NSError *error) {
@strongify(self);
if (!error) {
self.placemark = [placemarks firstObject];
geocodeBlock(((CLPlacemark *)[placemarks firstObject]).addressDictionary, nil);
} else {
geocodeBlock(nil, error);
}
}];
}
- (void)GeocodeAddressString: (NSString *)Place CompleteBlock: (didGeocodeAddress)geocodeBlock {
@weakify(self);
[self.geocoder geocodeAddressString:Place completionHandler:^(NSArray *placemarks, NSError *error) {
@strongify(self);
self.placemark = [placemarks firstObject];
geocodeBlock([placemarks firstObject], error);
}];
}
- (void)GeocodeAddressString:(NSString *)Place WithRegion:(CLRegion *)region CompleteBlock:(didGeocodeAddress)geocodeBlock {
@weakify(self);
[self.geocoder geocodeAddressString:Place inRegion:region completionHandler:^(NSArray *placemarks, NSError *error) {
@strongify(self);
self.placemark = [placemarks firstObject];
geocodeBlock([placemarks firstObject], error);
}];
}
- (void)MapView: (MKMapView *)mapView AddLineFrom: (CLPlacemark *)From to: (CLPlacemark *)To {
// 设置方向请求
MKDirectionsRequest * request = [[MKDirectionsRequest alloc] init];
// 设置起点终点
MKPlacemark * sourcePm = [[MKPlacemark alloc] initWithPlacemark:From];
request.source = [[MKMapItem alloc] initWithPlacemark:sourcePm];
MKPlacemark * destiPm = [[MKPlacemark alloc] initWithPlacemark:To];
request.destination = [[MKMapItem alloc] initWithPlacemark:destiPm];
//定义方向对象
MKDirections * dirs = [[MKDirections alloc] initWithRequest:request];
//计算路线
[dirs calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
DLogOut(@"总共有%lu条线路",(unsigned long)response.routes.count);
for (MKRoute *route in response.routes) {
[mapView addOverlay:route.polyline];
}
}];
/* 划线以及颜色
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay{
MKPolylineRenderer * renderer = [[MKPolylineRenderer alloc] initWithOverlay:overlay];
renderer.strokeColor = [UIColor redColor];
return renderer;
}
*/
}
- (CLLocationManager *)locationManager {
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.distanceFilter = 10; // 默认的定位服务更新频率, 米
}
return _locationManager;
}
- (CLGeocoder *)geocoder {
if (!_geocoder) {
_geocoder = [[CLGeocoder alloc] init];
}
return _geocoder;
}
#pragma mark -
#pragma mark Location Did Update
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation * currentLocation = [locations lastObject];
if (self.locationBlock) {
self.locationBlock(currentLocation, nil);
}
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
if (self.locationBlock) {
self.locationBlock(nil, error);
}
}
@end
文中的weakify以及strongify为了避免block的循环引用, 详细解释以及代码实现可以在这里看到Weakify&Strongify, 这个写法只是最常见的写法, 不过ReactCocoa中给出了一种装逼的写法, 大家去看一下, 使用宏把代码片段连到一起。 巧妙的使用宏可以帮助我们大大的简化代码并提高逼格。
上班第一天, 祝大家开心。
CopyRight@Dylan 2015-10-8.