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

简介: 第三方授权的应用苹果审核被驳回解决方案和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

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个小时上架的)。当然这个你的用户密码功能没有开发完的情况。若你的这个功能完全,直接不显示第三方授权登录的按钮,保留用户名密码按钮就可以。


目录
相关文章
|
2月前
|
移动开发 小程序 Android开发
基于 uni-app 开发的废品回收类多端应用功能与界面说明
本文将对一款基于 uni-app 开发的废品回收类多端应用,从多端支持范围、核心功能模块及部分界面展示进行客观说明,相关资源信息也将一并呈现。
137 0
|
1月前
|
移动开发 前端开发 Android开发
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
235 12
【02】建立各项目录和页面标准化产品-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
1月前
|
移动开发 JavaScript 应用服务中间件
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
203 5
【06】优化完善落地页样式内容-精度优化-vue加vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
1月前
|
移动开发 Rust JavaScript
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
514 4
【01】首页建立-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
|
1月前
|
移动开发 Android开发
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
【03】建立隐私关于等相关页面和内容-vue+vite开发实战-做一个非常漂亮的APP下载落地页-支持PC和H5自适应提供安卓苹果鸿蒙下载和网页端访问-优雅草卓伊凡
120 0
|
2月前
|
监控 数据挖掘 API
快应用报错Module Error 分包要求 app.json#minPlatformVersion 不小于 1061, 当前值为 21解决方案-优雅草卓伊凡
快应用报错Module Error 分包要求 app.json#minPlatformVersion 不小于 1061, 当前值为 21解决方案-优雅草卓伊凡
120 0
|
4月前
|
Java Shell Maven
【Azure Container App】构建Java应用镜像时候遇无法编译错误:ERROR [build 10/10] RUN ./mvnw.cmd dependency:go-offline -B -Dproduction package
在部署Java应用到Azure Container App时,构建镜像过程中出现错误:“./mvnw.cmd: No such file or directory”。尽管项目根目录包含mvnw和mvnw.cmd文件,但依然报错。问题出现在Dockerfile构建阶段执行`./mvnw dependency:go-offline`命令时,系统提示找不到可执行文件。经过排查,确认是mvnw文件内容异常所致。最终通过重新生成mvnw文件解决该问题,镜像成功构建。
164 1
|
4月前
|
存储 移动开发 监控
App Trace功能实战:一键拉起、快速安装与免提写邀请码的应用实践
App Trace系统通过一键拉起、快速安装和免提写邀请码三大功能,显著提升用户转化率、安装成功率和邀请注册率。结合深度技术实现与优化,助力公司用户增长,成为移动端核心基础设施。
|
6月前
|
搜索推荐 API UED
淘宝/天猫获得淘宝app商品详情原数据 API 返回值的应用
该API专注于商品信息整合与展示,提供基础信息抓取、多媒体内容整合等功能,助力实时同步商品数据,构建丰富的详情页。同时支持数据分析与市场洞察,包括销售趋势分析和竞品对比,优化库存与定价策略。此外,动态促销管理和个性化推荐系统可提升营销效果,而实时库存预警和评价数据可视化则显著增强用户体验,为用户决策提供透明依据,全面提升平台竞争力与用户满意度。
APP应用登录应用配置
本帖讲述的是App应用使用支付宝快捷登录的应用配置,分IOS和Android  第一步:获得一个应用Appid、功能签约 需要有一个应用,应用创建可以参考:官方开发文档应用创建  并对应用进行功能权限的添加,文档有:添加功能示例,我们要添加的是:APP支付宝登录 注意:这里显示需签约才能生效。
574 12

热门文章

最新文章