根據應用啟動時擷取的版本資訊來決定顯示還是隐藏第三方授權按鈕和使用者名密碼登入方式。
由于現在禁止app自己彈出版本更新的提示框,那麼絕大多數應用都需要版本更新的,甚至在強制更新場景下,以前的版本接口或邏輯都廢掉了,不更新就落下一地的雞毛。
蘋果商店那麼app怎麼都監控的到,也就是做做樣子,别在它稽核你新版本時版本更新就可以了。也可能蘋果的目的就是煩你在它稽核你的app時,你彈出版本更新,騷擾到它了。但是有的應用很特殊,隻允許微信等第三方軟體授權登入,沒有使用者名登入。那麼你上架時,蘋果商店稽核,絕對會給你no pass。因為蘋果不允許你的app依賴與其它app,不能跳轉到空白頁面,你給他一個沒有安裝微信等提示,它也不領情,仍舊殘忍的拒絕。那麼怎麼解決這種稽核時的雞生蛋,蛋生雞的問題呢?我們的app就時這樣的,被拒絕了三次。
解決方案是:采用啟動時版本更新查詢的方式。稽核前,把新app版本設定為手動釋出。把app目前版本号發送給伺服器,伺服器傳回目前最高版本号,是否強更新。最好有兩個标志:是否更新标志和是否強制更新标志。我們現在app處理比較簡單隻有一個是否更新标志,以前我做的曹操專車app是有兩個标志的。用兩個更新标志的好處是,可以實作小範圍試用的需求,當版本大改版本,可能風險時,出一個小範圍使用的試用版本,讓指定一批使用者非強制更新,其它絕大多數使用者不更新,一般運作幾天,收集客服回報過來的問題,看是否有重大風險,若有風險就删除該版本,讓使用者重新下載下傳以前的版本,若沒有風險就把該版本對全體沒有安裝最次版本的使用者提升為強制更新或非強制更新版本。畢竟有重大風險的機率有,但是不大,這樣也可以控制影響範圍。不過這個小範圍試用處理機制和我們現在問題有點沖突,不過可以通過增加标志解決。app拿到版本更新相關資料,和app版本号比較,若本地版本号高于伺服器的版本号,顯示稽核用的固定使用者名和密碼登入,隐藏第三方授權按鈕和圖示;若查詢失敗或伺服器傳回的版本号小于等于本地版本号就顯示正常的第三方授權登入按鈕。收到蘋果蘋果稽核通過郵件後,手動釋出app,并把伺服器傳回的版本号提高到最新版本,版本更新設定為不更新。收到上架成功的郵件,修改資料庫或修改版本更新配置讓版本更新查詢傳回強制更新或非強制更新。

一般app非第一次上架,從手動釋出版本到在蘋果商店看到該版本,通常需要2小時左右。當然蘋果客服的官方說法是24小時都是正常範圍内。我一個新應用就出現過30多小時在蘋果商店沒有看到情況,沒有辦法隻有自己拼接app下載下傳位址,手動激活才看到我的app,具體見我的文章《手動第一次上架的應用如何快速在蘋果商店看到》。
伺服器響應的日志:
-- ::: ArtEnjoymentWeChatAuction[:] AWUpdateVersionModel.m:AWUpdateVersionModel.m:-[AWUpdateVersionModel setupRACCommand]_block_invoke_3: 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 == )
{
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 [email protected]"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 = ; (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 > ) && (localVersionArr.count < versionArr.count) && (!isEmptyString(versionArr[versionArr.count - ]) && ([versionArr[versionArr.count - ] integerValue] > )))
{
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 - 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個小時上架的)。當然這個你的使用者密碼功能沒有開發完的情況。若你的這個功能完全,直接不顯示第三方授權登入的按鈕,保留使用者名密碼按鈕就可以。