AVOS漫谈、CLLocation、iOS9-Sample。

之前写过一片AVOS一些常用的使用, 比如用户注册, 登录, 数据的查询, 图片的加载以及初始化AVOS使用解析, 慢慢的在自己玩一些东西的过程中发现AVOS确实是一个不错的后端支撑, 简称Baas, 移动应用后端服务。

所以在慢慢的开发过程中, 使用RAC以及AVOS快速的建立一套登录体系, 以及数据的Fetch, 云代码的自定义与调用, AVObject与本地Model的相互转换也慢慢的熟悉了起来。

代码浅显易懂, 所以不做过多的解释了。

  1. 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;
}
  1. 自己写操作合集, 而不是直接调用.
    • 建议在使用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.

你可能感兴趣的:(AVOS漫谈、CLLocation、iOS9-Sample。)