前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS小技能:封装定位SDK,统一数据模型和错误处理。

iOS小技能:封装定位SDK,统一数据模型和错误处理。

作者头像
公众号iOS逆向
发布2022-08-22 11:45:00
8710
发布2022-08-22 11:45:00
举报
文章被收录于专栏:iOS逆向与安全

引言

需求背景:收款页面需要请求IP定位API获取经纬度,由于高德的API的库不准确(没有实时更新),使用公司决定换腾讯API。

本人推荐app侧的SDK定位使用高德,因为高德SDK定位更准确,错误信息也更详细。

app侧使用定位的相关功能:

  1. 商户进件APP:商户详情的重新定位,编辑进件信息时的商户地址定位、支付终端绑定的重新定位、新增拜访记录的定位、新增/编辑企业的企业地址定位
  2. 商户端APP:终端管理的设备定位、店铺定位。

I 单次定位

1.1 腾讯SDK(TencentLBS)

代码语言:javascript
复制
/**
 * 设置用户是否同意隐私协议政策
 * <p>调用其他接口前必须首先调用此接口进行用户是否同意隐私政策的设置,传入YES后才能正常使用定位功能,否则TencentLBSLocationManager初始化不成功,返回nil,定位功能均无法使用</p>
 * @param isAgree 是否同意隐私政策
 */
+ (void)setUserAgreePrivacy:(BOOL) isAgree;
/**
 *  单次定位
 *
 *  该方法为下面方法的一层封装。
 *  level默认是TencentLBSRequestLevelPoi
 *  timeout默认是10s
 */
- (BOOL)requestLocationWithCompletionBlock:(TencentLBSLocatingCompletionBlock)completionBlock;



// 先执行代理方法tencentLBSDidChangeAuthorization再执行此回调
/**
 * 当前属于模糊定位状态时,通过该接口请求暂时的完全定位精度的权限
 * @param purposeKey 需要在info.plist中配置NSLocationTemporaryUsageDescriptionDictionary key值和对应的申请该权限的描述理由
 * @param completion 在弹框让用户选择后的用户的反馈,如果用户授予该权限,block中的参数为nil,如果未授予,block中的参数将为PurposeKey对于的key的描述(如PurposeKey=TemporaryPurposKey_1)
 */
- (void)requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(NSString *)purposeKey
                                                     completion:(void (^)(NSError *))completion;

初始化locationManager

代码语言:javascript
复制
- (TencentLBSLocationManager *)locationManager{

    if(_locationManager == nil){
        _locationManager = [[TencentLBSLocationManager alloc] init];


        [self configLocationManager];


    }


    return _locationManager;


}

- (void)configLocationManager {
    [TencentLBSLocationManager setUserAgreePrivacy:YES];

  _locationManager = [[TencentLBSLocationManager alloc] init];

    [_locationManager setDelegate:self];
    [_locationManager setPausesLocationUpdatesAutomatically:NO];
//    [_locationManager setAllowsBackgroundLocationUpdates:YES];
    [_locationManager setApiKey:@"-----6JBM3"];//
//
    //若获取的drLocatin中带有地址信息,可
    [_locationManager setRequestLevel:TencentLBSRequestLevelAdminName];
    

//处理首次定位,原生API处理首次定位(推荐)QCTLocationServiceUtil isHasLocationAuthorityWithisShowAlert:YES block
    //不使用SDK的方法
//    CLAuthorizationStatus authorizationStatus= [CLLocationManager authorizationStatus];
//    if (authorizationStatus == kCLAuthorizationStatusNotDetermined) {
//        [self.locationManager requestWhenInUseAuthorization];
//    }
//
    
}


单次定位

代码语言:javascript
复制
- (void)SingleLocation:(TencentLBSLocatingCompletionBlock)completionBlock{
    
    
__weak __typeof__(self) weakSelf = self;
    
    
    
//处理首次定位
[QCTLocationServiceUtil isHasLocationAuthorityWithisShowAlert:YES block:^(id  _Nonnull sender) {
    
    //模糊定位适配
    [weakSelf setuprequestLocationWithCompletionBlock:completionBlock];
    
    
}];
    
    
    

}

- (void)setuprequestLocationWithCompletionBlock:(TencentLBSLocatingCompletionBlock)completionBlock{
    
    
        
    self.block =completionBlock;// 用于适配iOS14
    
    
    [SVProgressHUD showWithStatus:@"定位中.."];
    
    //1.iOS 模糊定位适配
//    可以使用以下方法判断当前应用的定位精度权限,业务可根据相应的值做出不同的操作:
    if (@available(iOS 14.0, *)) {
        TencentLBSAccuracyAuthorization accAuthor = [TencentLBSLocationManager accuracyAuthorization];

        if(accAuthor == TencentLBSAccuracyAuthorizationReducedAccuracy){
            
    //        当前属于模糊定位状态时,通过该接口请求暂时的完全定位精度的权限

            //权限的变更会通过TencentLBSLocationManagerDelegate中的 - (void)tencentLBSDidChangeAuthorization:(TencentLBSLocationManager *)manager方法回调
            
                                self.isrequestTemporaryFullAccuracyAuthorizationWithPurposeKey = YES;
                    // 记录当前是否向用户申请临时开启一次精确位置权限,用于【如果定位精度权限变更为精确的时候,再次更新定位信息】

            [self.locationManager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:(  @"purposeKey4changeInfo") completion:^(NSError * _Nonnull err) {
                
                
                // * @param completion 在弹框让用户选择后的用户的反馈,如果用户授予该权限,block中的参数为nil,如果未授予,block中的参数将为PurposeKey对于的key的描述(如PurposeKey=TemporaryPurposKey_1)
// 先执行代理方法tencentLBSDidChangeAuthorization再执行此回调
                if(!err){//未授予,则直接定位
                    
                    
                    [self requestLocation];

                    
                }

                
            }];

            return ;
        }
        

        
    }
    // 2. 调用单次定位
    
    [self requestLocation];

}

- (void)requestLocation{
//    [self configLocationManager ];
    
    
    [self.locationManager requestLocationWithCompletionBlock:
        ^(TencentLBSLocation *location, NSError *error) {
            NSLog(@"%@, %@, %@", location.location, location.name, location.address);
        [SVProgressHUD dismiss];

        if(self.block){
            
            
            self.block(location,error);
            
            
            
        }

        }];
    
    

    
}

/**
 *  定位权限状态改变时回调函数
 *  @param manager 定位 TencentLBSLocationManager 类,由此访问authorizationStatus,accuracyAuthorization
 */
- (void)tencentLBSDidChangeAuthorization:(TencentLBSLocationManager *)manager{
    
    //    - 如果定位精度权限变更为精确的时候,再次更新定位信息

    if (@available(iOS 14.0, *)) {
        if(    self.locationManager.accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy){
            
            
            
            
            if(       self.isrequestTemporaryFullAccuracyAuthorizationWithPurposeKey == YES){// 首次请求
                
                self.isrequestTemporaryFullAccuracyAuthorizationWithPurposeKey = NO;
                
                // 获取定位
                [self requestLocation];
                
                
            }
            
            
            
            
            
            
            
        }
    } else {

}
    
    
    
}

    

注意:仅当TencentLBSRequestLevel为TencentLBSRequestLevelPoi有返回值,否则为空。

代码语言:javascript
复制
/**
 *  返回当前位置周围的POI
 *  仅当TencentLBSRequestLevel为TencentLBSRequestLevelPoi有返回值,否则为空
 */
@property (nonatomic, strong, nullable) NSArray<TencentLBSPoi*> *poiList;


错误信息

代码语言:javascript
复制
typedef NS_ENUM(NSUInteger, TencentLBSLocationError) {
    TencentLBSLocationErrorUnknown = 0,                 //!< 错误码,表示目前位置未知,但是会一直尝试获取
    TencentLBSLocationErrorDenied = 1,                  //!< 错误码,表示定位权限被禁止
    TencentLBSLocationErrorNetwork = 2,                 //!< 错误码,表示网络错误
    TencentLBSLocationErrorHeadingFailure = 3,          //!< 错误码,表示朝向无法确认
    TencentLBSLocationErrorOther = 4,                   //!< 错误码,表示未知错误
};

1.2 高德SDK

  1. 添加依赖 pod 'AMapLocation', '2.6.7'
  2. 导入头文件:``
代码语言:javascript
复制
- (void)setuprequestLocationWithAMapLocatingCompletionBlock:(AMapLocatingCompletionBlock)completionBlock{
        
    self.block =completionBlock;
    self.location = [[AMapLocationManager alloc]init];
    self.location.delegate = self;
    
    [SVProgressHUD showWithStatus:@"定位中.."];

    if (@available(iOS 14.0, *)) {
        self.location.locationAccuracyMode = AMapLocationFullAndReduceAccuracy;
    } else {
        // Fallback on earlier versions
    }
        
    [self.location setDesiredAccuracy:kCLLocationAccuracyHundredMeters];
    //   定位超时时间,最低2s,此处设置为2s
    self.location.locationTimeout = 2;
    //   逆地理请求超时时间,最低2s,此处设置为2s
    self.location.reGeocodeTimeout = 2;
    
    [self.location requestLocationWithReGeocode:YES completionBlock:^(CLLocation *location, AMapLocationReGeocode *regeocode, NSError *error) {
       
            [SVProgressHUD dismiss];
        

        if(completionBlock){
            
            completionBlock(location,regeocode,error);
            
            
        }
    }];

}

错误信息分析

代码语言:javascript
复制
///AMapLocation errorCode
typedef NS_ENUM(NSInteger, AMapLocationErrorCode)
{
    AMapLocationErrorUnknown = 1,               ///<未知错误
    AMapLocationErrorLocateFailed = 2,          ///<定位错误
    AMapLocationErrorReGeocodeFailed  = 3,      ///<逆地理错误
    AMapLocationErrorTimeOut = 4,               ///<超时
    AMapLocationErrorCanceled = 5,              ///<取消
    AMapLocationErrorCannotFindHost = 6,        ///<找不到主机
    AMapLocationErrorBadURL = 7,                ///<URL异常
    AMapLocationErrorNotConnectedToInternet = 8,///<连接异常
    AMapLocationErrorCannotConnectToHost = 9,   ///<服务器连接失败
    AMapLocationErrorRegionMonitoringFailure=10,///<地理围栏错误
    AMapLocationErrorRiskOfFakeLocation = 11,   ///<存在虚拟定位风险
    AMapLocationErrorNoFullAccuracyAuth = 12,   ///<精确定位权限异常
};

II 判断经纬度是否在国内

2.1 判断经纬度是否在国内

代码语言:javascript
复制
TencentLBSLocationUtils
/**
 *  判断经纬度是否在国内
 *  
 */
+ (BOOL) isInRegionWithLatitude:(double)latitude longitude:(double)longitude;


https://blog.csdn.net/z929118967/article/details/120510396

封装

代码语言:javascript
复制

/**
 
 根据经纬度判断 是否在大陆地区
 */
+ (BOOL)inChineseMainlandWithCLLocation:(CLLocation *)location province:(NSString *)province{
    //coordinate.longitude
    
    if([TencentLBSLocationUtils isInRegionWithLatitude:location.coordinate.latitude longitude:location.coordinate.longitude]){
        
        // 排除 香港、澳门、台湾
        if([province isEqualToString:@"香港特别行政区"] || [province isEqualToString:@"澳门特别行政区"] || [province isEqualToString:@"台湾"]){
            
            return NO;

            
        }else{
            return YES;

        }
        
        
        
    }else{//其他地区
        
        return NO;

    }
    return YES;
}


2.2 根据定位返回信息判断当前位置的行政区

代码语言:javascript
复制
/**
 *  返回当前位置的行政区划, 0-表示中国大陆、港、澳, 1-表示其他
 */
@property (nonatomic, assign) NSInteger areaStat;


III 封装定位SDK,统一数据模型

为了便于灵活切换,新增定位统一入口,用于封装腾讯SDK和高德SDK,便于灵活切换。

代码语言:javascript
复制
        [CRMLBSManager singleLocationIsShowError:YES WithcompletionBlock:^(CRMLBSLocationModel * _Nonnull location) {
            
        
            if(location.error){
                return;
                
            }
            
            if(!location.isInRegion){
                [SVProgressHUD showInfoWithStatus:@"您最新定位不在支持范围内!"];
                return;

                
            }            
                    weakSelf.locationView.adressLab.text = location.address;

        }];
    }];
    

3.1 定位数据模型

代码语言:javascript
复制
@interface CRMLBSLocationModel : NSObject

/**
 *  返回当前位置的CLLocation信息
 */
@property (nonatomic, strong) CLLocation *location;


/**
 是否在国内,排除港、澳、台
 */
@property (nonatomic, assign) BOOL isInRegion;

/**
 *  返回当前位置的地址
 */
@property (nonatomic, copy, nullable) NSString *address;



/**
 *  返回当前位置的省份
 */
@property (nonatomic, copy, nullable) NSString *province;


/**
 *  返回当前位置的街道
 */
@property (nonatomic, copy, nullable) NSString *street;

/**
 *  返回当前位置的街道编码
 */
@property (nonatomic, copy, nullable) NSString *street_no;


///兴趣点名称
@property (nonatomic, copy) NSString *POIName;

@property (nonatomic, strong) NSError  *error;

@end

3.2 封装腾讯SDK的定位数据

代码语言:javascript
复制
/**
 isShowError: 发生错误是否展示提示语
 completionBlock: 定位回调
 */
+ (void)singleLocationIsShowError:(BOOL)isShowError WithcompletionBlock:(void (^)(CRMLBSLocationModel * _Nonnull location))completionBlock{
    
    [ERPLBS.shareERPLBS SingleLocation:^(TencentLBSLocation * _Nullable location, NSError * _Nullable error) {
        if ([SVProgressHUD isVisible]) {
            [SVProgressHUD dismiss];
        }

        CRMLBSLocationModel *model = [CRMLBSLocationModel new];
        
        if(error){//TencentLBSLocationError
            
            NSLog(@"locError:{%ld - %@};", (long)error.code, error.localizedDescription);

            if(isShowError){
                NSString *errorInfo = error.userInfo[@"NSLocalizedDescription"];
                errorInfo=errorInfo?errorInfo:@"定位失败请重新再试!";
    
                [SVProgressHUD showErrorWithStatus:errorInfo];
                //           [self showHUDMessage:errorInfo];
                
            }
            
            model.error = error;
            if(completionBlock){
                completionBlock(model);
            }
            return;
        }
        
        // 定位成功

        if (!location)
        {
            if(completionBlock){
                completionBlock(model);
            }
            return;
        }
        NSLog(@"location:%@", location);

        model.street = location.street;
        model.street_no = location.street_no;
        model.address= location.address;
        model.location = location.location;//经纬度
        model.POIName =[location.poiList.firstObject name];//[ERPLBS POInamebyArr:location.poiList]
        model.province = location.province;//
        // 判断是否在大陆 areaStat
        model.isInRegion= [ERPLBS inChineseMainlandWithCLLocation:location.location province:location.province];
        if(completionBlock){
            completionBlock(model);
        }

    }];
    
    
}


3.3 封装高德SDK的定位数据

代码语言:javascript
复制
+ (void)singleLocation4AMapIsShowError:(BOOL)isShowError WithcompletionBlock:(void (^)(CRMLBSLocationModel * _Nonnull location))completionBlock{
    
    
    [[ProjectMethod shareProjectMethod] SingleLocation:^(CLLocation *location, AMapLocationReGeocode *regeocode, NSError *error) {
        
        if ([SVProgressHUD isVisible]) {
            [SVProgressHUD dismiss];
        }

        CRMLBSLocationModel *model = [CRMLBSLocationModel new];

        if(error){//AMapLocationErrorCode
            
            NSLog(@"locError:{%ld - %@};", (long)error.code, error.localizedDescription);

            if(isShowError){
                NSString *errorInfo = error.userInfo[@"NSLocalizedDescription"];
                errorInfo=errorInfo?errorInfo:@"定位失败请重新再试!";
    
                [SVProgressHUD showErrorWithStatus:errorInfo];
                //           [self showHUDMessage:errorInfo];
                
            }
            
            model.error = error;
            if(completionBlock){
                completionBlock(model);
            }
            return;
        }
        
        // 定位成功

        if (!location)
        {
            if(completionBlock){
                completionBlock(model);
            }
            return;
        }
        NSLog(@"location:%@", location);

        if (!regeocode)
        {
            if(completionBlock){
            completionBlock(model);
        }
            return;
            
        }
        NSLog(@"reGeocode:%@", regeocode);

        // 判断是否在大陆
        model.isInRegion= [ERPAMapLocationTool inChineseMainlandWithCLLocation:location regeocode:regeocode];
        
        model.location = location;//经纬度
        
        model.street = regeocode.street;
        
        model.street_no = regeocode.number;


        model.address=[NSString stringWithFormat:@"%@%@%@%@%@",regeocode.province,regeocode.city,regeocode.district,regeocode.street,regeocode.POIName];
        //regeocode.formattedAddress;//
        model.POIName =regeocode.POIName;
    
        model.province = regeocode.province;//

        if(completionBlock){
            completionBlock(model);
        }
        
    }];
    
    
    
}

3.4 统一定位入口

代码语言:javascript
复制
#define k_TENCENTLBS NO// 控制定位类型

/**
 isShowError: 发生错误是否展示提示语
 completionBlock: 定位回调
 */
+ (void)singleLocationIsShowError:(BOOL)isShowError WithcompletionBlock:(void (^)(CRMLBSLocationModel * _Nonnull location))completionBlock{
    NSLog(@"统一定位入口");
    
    //
    
    if(!k_TENCENTLBS){
        // 调用高德SDK
        NSLog(@"调用高德SDK");
        [self singleLocation4AMapIsShowError:isShowError WithcompletionBlock:completionBlock];
        return;
    }
    NSLog(@"调用腾讯SDK");
}
    

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iOS逆向 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • I 单次定位
    • 1.1 腾讯SDK(TencentLBS)
      • 1.2 高德SDK
      • II 判断经纬度是否在国内
        • 2.1 判断经纬度是否在国内
          • 2.2 根据定位返回信息判断当前位置的行政区
          • III 封装定位SDK,统一数据模型
            • 3.1 定位数据模型
              • 3.2 封装腾讯SDK的定位数据
                • 3.3 封装高德SDK的定位数据
                  • 3.4 统一定位入口
                  相关产品与服务
                  云服务器
                  云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档