第三方授权的应用苹果审核被驳回解决方案和app版本更新

根据应用启动时获取的版本信息来决定显示还是隐藏第三方授权按钮和用户名密码登录方式。
由于现在禁止app自己弹出版本更新的提示框,那么绝大多数应用都需要版本更新的,甚至在强制更新场景下,以前的版本接口或逻辑都废掉了,不更新就落下一地的鸡毛。
苹果商店那么app怎么都监控的到,也就是做做样子,别在它审核你新版本时版本更新就可以了。也可能苹果的目的就是烦你在它审核你的app时,你弹出版本更新,骚扰到它了。但是有的应用很特殊,只允许微信等第三方软件授权登录,没有用户名登录。那么你上架时,苹果商店审核,绝对会给你no pass。因为苹果不允许你的app依赖与其它app,不能跳转到空白页面,你给他一个没有安装微信等提示,它也不领情,仍旧残忍的拒绝。那么怎么解决这种审核时的鸡生蛋,蛋生鸡的问题呢?我们的app就时这样的,被拒绝了三次。
解决方案是:采用启动时版本更新查询的方式。审核前,把新app版本设置为手动发布。把app当前版本号发送给服务器,服务器返回当前最高版本号,是否强更新。最好有两个标志:是否更新标志和是否强制更新标志。我们现在app处理比较简单只有一个是否更新标志,以前我做的曹操专车app是有两个标志的。用两个升级标志的好处是,可以实现小范围试用的需求,当版本大改版本,可能风险时,出一个小范围使用的试用版本,让指定一批用户非强制更新,其它绝大多数用户不更新,一般运行几天,收集客服反馈过来的问题,看是否有重大风险,若有风险就删除该版本,让用户重新下载以前的版本,若没有风险就把该版本对全体没有安装最次版本的用户提升为强制更新或非强制更新版本。毕竟有重大风险的概率有,但是不大,这样也可以控制影响范围。不过这个小范围试用处理机制和我们现在问题有点冲突,不过可以通过增加标志解决。app拿到版本更新相关数据,和app版本号比较,若本地版本号高于服务器的版本号,显示审核用的固定用户名和密码登录,隐藏第三方授权按钮和图标;若查询失败或服务器返回的版本号小于等于本地版本号就显示正常的第三方授权登录按钮。收到苹果苹果审核通过邮件后,手动发布app,并把服务器返回的版本号提高到最新版本,版本更新设置为不更新。收到上架成功的邮件,修改数据库或修改版本更新配置让版本更新查询返回强制更新或非强制更新。
这里写图片描述
一般app非第一次上架,从手动发布版本到在苹果商店看到该版本,通常需要2小时左右。当然苹果客服的官方说法是24小时都是正常范围内。我一个新应用就出现过30多小时在苹果商店没有看到情况,没有办法只有自己拼接app下载地址,手动激活才看到我的app,具体见我的文章《手动第一次上架的应用如何快速在苹果商店看到》
服务器响应的日志:

2018-09-04 15:11:52:233 ArtEnjoymentWeChatAuction[57332:5293160] AWUpdateVersionModel.m:AWUpdateVersionModel.m:-[AWUpdateVersionModel setupRACCommand]_block_invoke_3:91 Verbose:data:{
    appCode = agent;
    appId = 4;
    appName = "\U827a\U4eab\U4f18\U9009";
    remark = "";
    type = ios;
    update = N;
    version = "0.0.1";
}

版本更新的部分代码如下:

- (AWUpdateVersionModel *)updateVersionModel
{
    if (_updateVersionModel == nil) {
        _updateVersionModel = [[AWUpdateVersionModel alloc]init];
    }
    return _updateVersionModel;
}

- (AWUpdateVersionEntity *)updateVersionEntity
{
    if (_updateVersionEntity == nil) {
        NSDictionary *localDic = [[NSBundle mainBundle] infoDictionary];
        NSString *localVersion = [localDic objectForKey:@"CFBundleShortVersionString"];
        _updateVersionEntity =[AWUpdateVersionEntity updateVersionEntityWithLocalVersion:localVersion];
    }
    return _updateVersionEntity;
}
- (void)forceUpdte
{
    @weakify(self);
    FLDDLogVerbose(@"requestUrl:%@,params:%@",@"api/version/validateUpdate.ns", self.updateVersionEntity);
    [[self.updateVersionModel.validateUpdateCommand execute:self.updateVersionEntity]subscribeNext:^(AWUpdateVersionEntity *resultUpdateVersionEntity) {
        @strongify(self);
        FLDDLogVerbose(@"resultUpdateVersionEntity:%@", resultUpdateVersionEntity);
        if((resultUpdateVersionEntity == nil) || ![resultUpdateVersionEntity isKindOfClass:[AWUpdateVersionEntity class]])
        {
            return;
        }
        if((!resultUpdateVersionEntity.isNeedUpdate) || !((resultUpdateVersionEntity.updateType == UPDATE_TYPE_REMIND) || (resultUpdateVersionEntity.updateType == UPDATE_TYPE_FORCE)))
        {
            return;
        }
        self.updateVersionEntity = resultUpdateVersionEntity;
        [AWUpdateVersionView initWithUpdateVersionEntity:resultUpdateVersionEntity];
    } error:^(NSError *error) {
    }];
}

AWUpdateVersionEntity.h文件

#import <Foundation/Foundation.h>
#import "AWUpdateVersionMacro.h"

@interface AWUpdateVersionEntity : NSObject
@property(nonatomic, strong) NSString *download;
@property(nonatomic, strong) NSString *appCode;
@property(nonatomic, strong) NSString *appName;
@property(nonatomic, strong) NSString *remark;
@property(nonatomic, strong) NSString *update;
@property(nonatomic, strong) NSString *version;
@property(nonatomic, strong) NSString *localVersion;
@property(nonatomic, strong) NSString *errorInfo;
@property(nonatomic, assign) NSString *appId;
@property(nonatomic, assign) BOOL isNeedUpdate;
@property(nonatomic, assign) BOOL isUpdateData;
@property(nonatomic, assign) BOOL isLargerCurrentVersion;
@property(nonatomic, assign) UPDATE_TYPE updateType;



+ (instancetype)updateVersionEntityWithLocalVersion:(NSString *)localVersion;

- (void)updateVersionEntityWithNewUpdateVersionEntity:(AWUpdateVersionEntity *)newUpdateVersionEntity;
@end

AWUpdateVersionEntity.m文件

#import "AWUpdateVersionEntity.h"

@implementation AWUpdateVersionEntity
+ (instancetype)updateVersionEntityWithLocalVersion:(NSString *)localVersion;
{
    return [[self alloc] initWithLocalVersion:(NSString *)localVersion];
}

- (instancetype)initWithLocalVersion:(NSString *)localVersion
{
    if (self = [super init]) {
        _localVersion= localVersion;
        _errorInfo = nil;
        _appCode = nil;
        _appName = nil;
        _remark = @"";
        _update = nil;
        _version = nil;
        _appId = @"1";
        _download = nil;
        _isNeedUpdate = NO;
        _updateType = UPDATE_TYPE_NO;
        _isUpdateData = NO;
    }
    return self;
}


- (void)updateVersionEntityWithNewUpdateVersionEntity:(AWUpdateVersionEntity *)newUpdateVersionEntity
{
    if(!newUpdateVersionEntity || ![newUpdateVersionEntity isKindOfClass:[AWUpdateVersionEntity class]])
    {
        return;
    }
    _download = newUpdateVersionEntity.download;
    _localVersion= newUpdateVersionEntity.localVersion;
    _errorInfo = newUpdateVersionEntity.errorInfo;
    _appCode = newUpdateVersionEntity.appCode;
    _appName = newUpdateVersionEntity.appName;
    _remark = newUpdateVersionEntity.remark;
    _update = newUpdateVersionEntity.update;
    _version = newUpdateVersionEntity.version;
    _appId = newUpdateVersionEntity.appId;
    _isNeedUpdate = newUpdateVersionEntity.isNeedUpdate;
    _updateType = newUpdateVersionEntity.updateType;
    _isUpdateData = newUpdateVersionEntity.isUpdateData;
}
@end

AWUpdateVersionModel.h文件:

#import <Foundation/Foundation.h>
@class AWUpdateVersionEntity;
@interface AWUpdateVersionModel : NSObject
/**
 *  版本更新
 */
@property(nonatomic,strong)RACCommand *validateUpdateCommand;

/**
 *  版本更新实体
 */
@property(nonatomic,strong)AWUpdateVersionEntity *updateVersionEntity;

@property (nonatomic, strong) YXRequestApi *validateUpdateInfoApi;
@end

AWUpdateVersionModel.m文件:

#import "AWUpdateVersionModel.h"
#import "AWUpdateVersionEntity.h"
#import "AWAppIconTypeEntity.h"

@implementation AWUpdateVersionModel
- (instancetype)init
{
    if (self = [super init]) {
        [self setupRACCommand];
    }
    return self;
}

-(YXRequestApi *)validateUpdateInfoApi{
    if (!_validateUpdateInfoApi)
    {
        _validateUpdateInfoApi = [[YXRequestApi alloc]init];
        _validateUpdateInfoApi
        .setBaseURL(kBaseURL)
        .setApiPath(@"market/app/version.htm")
        .setShowHUD(NO)
        .setRequestMethodType(YX_Request_POST)
        .setParams(@{});
    }
    return _validateUpdateInfoApi;
}

- (void)setupRACCommand
{
    @weakify(self);
    self.validateUpdateCommand = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            @strongify(self);
            if([input isKindOfClass:[AWUpdateVersionEntity class]])
            {
                self.updateVersionEntity = input;
            }
            else
            {
                [[AWNoticeView currentNotice] showErrorNotice:@"参数错误"];
                self.updateVersionEntity.errorInfo  = @"";
                [subscriber sendError:nil];
                return nil;
            }

            NSMutableDictionary *dic = [NSMutableDictionary new];
            [dic setSafeObject:@"ios" forKey:@"type"];
            [dic setSafeObject:APP_CODE forKey:@"appCode"];
            self.validateUpdateInfoApi.setParams(dic);
            [[YXRequestClient sharedClient] loadDataWithApi:self.validateUpdateInfoApi successBlock:^(NSDictionary *data) {
                @strongify(self);
                if((data == nil) || ![data isKindOfClass:[NSDictionary class]])
                {
                    [[AWNoticeView currentNotice] showErrorNotice:@"参数错误"];
                    self.updateVersionEntity.errorInfo  = @"";
                    [subscriber sendError:nil];
                    return;
                }
                AWUpdateVersionEntity *resultEntity = [AWUpdateVersionEntity yy_modelWithDictionary:data];
                NSString *localVersion = self.updateVersionEntity.localVersion;
                if(localVersion.length == 0)
                {
                    NSDictionary *localDic = [[NSBundle mainBundle] infoDictionary];
                    localVersion = [localDic objectForKey:@"CFBundleShortVersionString"];
                    self.updateVersionEntity.localVersion = localVersion;
                }
                resultEntity.localVersion = self.updateVersionEntity.localVersion;
                resultEntity.isUpdateData = YES;
                FLDDLogVerbose(@"data:%@", data);
//                resultEntity.update =@"N";
                if((resultEntity.update) && [resultEntity.update isKindOfClass:[NSString class]] && (resultEntity.version) && [resultEntity.version isKindOfClass:[NSString class]])
                {
                    NSArray *localVersionArr = [resultEntity.localVersion componentsSeparatedByString:@"."];
                    NSArray *versionArr = [resultEntity.version componentsSeparatedByString:@"."];
                    BOOL sameFlag = YES;
                    for(NSUInteger i = 0; (i < localVersionArr.count) && i < versionArr.count; i++)
                    {
                        if([versionArr[i] compare:localVersionArr[i]] == NSOrderedDescending)
                        {
                            resultEntity.isNeedUpdate = YES;
                            if([resultEntity.update isEqualToString:@"Y"])
                            {
                                resultEntity.updateType = UPDATE_TYPE_FORCE;
                            }
                            else
                            {
                                resultEntity.updateType = UPDATE_TYPE_REMIND;
                            }
                            sameFlag = NO;
                            break;
                        }
                        else if([versionArr[i] compare:localVersionArr[i]] == NSOrderedAscending)
                        {
                            resultEntity.isNeedUpdate = NO;
                            resultEntity.updateType = UPDATE_TYPE_NO;
                            sameFlag = NO;
                            resultEntity.isLargerCurrentVersion = YES;
//                            [[NSNotificationCenter defaultCenter] postNotificationName:updateLoginButtonNotification  object:@{@"isLargerCurrentVersion":@(YES)}];
                            [[NSNotificationCenter defaultCenter] postNotificationName:updateLoginButtonNotification object:nil userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @(YES),@"isLargerCurrentVersion", nil]];
                            break;
                        }
                    }
                    if(!(resultEntity.isNeedUpdate) && sameFlag && (localVersionArr.count > 0) && (localVersionArr.count < versionArr.count) && (!isEmptyString(versionArr[versionArr.count - 1]) && ([versionArr[versionArr.count - 1] integerValue] > 0)))
                    {
                        resultEntity.isNeedUpdate = YES;
                        if((resultEntity.update) && [resultEntity.update isKindOfClass:[NSString class]] && [resultEntity.update isEqualToString:@"Y"])
                        {
                            resultEntity.updateType = UPDATE_TYPE_FORCE;
                        }
                        else
                        {
                            resultEntity.updateType = UPDATE_TYPE_REMIND;
                        }
                        sameFlag = NO;
                    }

                }
                if(UPDATE_TYPE_REMIND == resultEntity.updateType)
                {
                    NSString *lastRemindUpdateTimeStr = [AWGeneralFunction valueForKey:@"remindUpdateTime"];
                    //实例化一个NSDateFormatter对象
                    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
                    //设定时间格式,这里可以设置成自己需要的格式
                    [dateFormatter setDateFormat:@"yyyy-MM-dd"];
                    //用[NSDate date]可以获取系统当前时间
                    NSString *currentDateStr = [dateFormatter stringFromDate:[NSDate date]];

                    if(lastRemindUpdateTimeStr)
                    {
                        if(lastRemindUpdateTimeStr && currentDateStr && [lastRemindUpdateTimeStr isEqualToString:currentDateStr])
                        {
                            resultEntity.updateType = UPDATE_TYPE_NO;
                        }
                    }
                    [AWGeneralFunction setUserDefaultsValue:currentDateStr key:@"remindUpdateTime"];
                }
                self.updateVersionEntity.errorInfo  = @"";
                [self.updateVersionEntity updateVersionEntityWithNewUpdateVersionEntity:resultEntity];
                //self.updateVersionEntity.remark = @"1.全面支持“#”话题,给喜欢的作品打个标签吧\n2.支持查看更多精彩评论\n3.支持查看更多精彩评论\n1.全面支持“#”话题,给喜欢的作品打个标签吧\n2.支持查看更多精彩评论\n3.支持查看更多精彩评论\n1.全面支持“#”话题,给喜欢的作品打个标签吧\n2.支持查看更多精彩评论\n3.支持查看更多精彩评论";
                [subscriber sendNext:self.updateVersionEntity];
                [subscriber sendCompleted];

            } failureBlock:^(NSError *error) {

                [[AWNoticeView currentNotice] showErrorNotice:error.domain];
                [subscriber sendError:error];
            }];
            return nil;
        }];
    }];
}

@end

下面是我当时遇到这个第三方授权登录被驳回,写的分析和报告:
只支持微信等第三方软件授权登录,并且不支持网页授权登录或用户名密码登录。

Guideline 4.2.3 - Design - Minimum Functionality 
We were required to install the WeChat app before we could log in via WeChat. Users should be able to log in with WeChat and access their accounts without having to install any additional apps. 
Next Steps 
If you would like to offer authentication through WeChat, please use a mechanism that allows users to log in with WeChat from within your app without first having to install an additional app. 
We recommend implementing the Safari View Controller API to display web content within your app. The Safari View Controller allows the display of a URL and inspection of the certificate from an embedded browser in an app so that customers can verify the webpage URL and SSL certificate to confirm they are entering their sign in credentials into a legitimate page. 

可以看到流行的软件当发现本地没有装微信时,都不显示微信登录;QQ当没有安装,大都也不显示QQ登录图标,个别的显示QQ图标并且采用网页登录QQ授权,不过他们都有用户名密码登录功能。所以它的常规解决方案时当发现本地没有安装第三方授权软件直接不显示它的按钮(按钮灰化不使能或弹出提示苹果照样给你驳回),就时没有完整的用户名/密码登录功能,至少给他在审核期间可以只有审核人员知道的登录账户和密码。可以当发现服务器返回版本低于app版本时显示临时用户名密码登录,发布时选择手动发布。当审核通过时(苹果会发邮件一般造成7点左右审核过的可能比较多),修改服务器返回的最新版本号并设置不更新,并发布版本,等在所在区域的苹果商店看到最新的app时在,再设置为非强制更新,等一天后等全世界的区域都上架后若是需要强制更新的再设置为强制更新(世界各个苹果市场上架时间并不一致,有不同的负责任,是苹果审核人员手动上架的,一般2小时上架,也有时6个小时上架的)。当然这个你的用户密码功能没有开发完的情况。若你的这个功能完全,直接不显示第三方授权登录的按钮,保留用户名密码按钮就可以。

猜你喜欢

转载自blog.csdn.net/jia12216/article/details/82384811