需求如下:
先說下使用場景和需求:
1)登錄時版本更新覆劈,用戶啟動app登錄或自動登錄時疚脐,發(fā)送請求需要帶本應(yīng)用的當(dāng)前版本號忙灼,服務(wù)返回的結(jié)果中包含版本更新信息杭攻,并且一般立刻彈出提示讓用戶選擇,我們會分為兩種猫态,一種是強制更新(左圖)一種是非強制更新庶溶。
2)在我的→設(shè)置→檢查更新 模塊中進(jìn)行版本的更新。
思路:
版本更新一般分為:強制更新和非強制性更新懂鸵。
強制更新的本質(zhì)是由服務(wù)器來判斷偏螺,需要拿當(dāng)前版本號(服務(wù)器數(shù)據(jù)庫中存儲的應(yīng)用版本號)和應(yīng)用發(fā)送的版本號相比較。
具體實現(xiàn)如下:
我們項目是組件式開發(fā)匆光,因此這部分功能的實現(xiàn)套像,我們放到了Launch模塊組件中。
先創(chuàng)建一個服務(wù)單例终息,其中包含(開啟第三方夺巩、登錄成功之后數(shù)據(jù)初始化贞让、Jpush、Bugly柳譬、WX喳张、UM、IQ美澳、RN销部、配置token相關(guān)信息、token刷新無效 則跳轉(zhuǎn)登錄)等相關(guān)服務(wù)制跟。
本篇文章只針對檢查版本更新做介紹舅桩。
在程序開始啟動的時候,增加如下代碼
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyWindow];
[[SCLaunchService shareInstance]startSCServices:nil];
[[SCLaunchService shareInstance] openThirdServices:launchOptions block:nil env:SC_Env_Type];
[self showLaunchView];
[[SCLaunchService shareInstance] checkAppVersion:nil];
return YES;
}
其中:
[[SCLaunchService shareInstance] checkAppVersion:nil];
這個功能就是程序一啟動就會檢查版本情況
#import <Foundation/Foundation.h>
#import "smartSDKHeader.h"
#import "SCEnvParameterConfig.h"
@interface SCLaunchService : NSObject
+ (instancetype)shareInstance;
/**
開啟服務(wù)(登錄成功后數(shù)據(jù)初始化)
*/
- (void)startSCServices:(VOIDBLOCK)started;
/**
開啟三方服務(wù)
@param started started description
*/
- (void)openThirdServices:(NSDictionary *)launchOptions block:(VOIDBLOCK)started env:(KEnvType)envType;
@interface SCLaunchService ()<JPUSHRegisterDelegate,JoyRouteProtocol>
@end
@implementation SCLaunchService
//用于登錄頁面完成之后相關(guān)服務(wù)的開啟
-(void)routeParam:(NSDictionary *)param block:(JoyRouteBlock)block{
if ([[param objectForKey:@"type"] isEqualToString:@"startSCServices"]) {
[self startSCServices:^{
block?block(param,nil):nil;
}];
}
}
//創(chuàng)建一個單例
static SCLaunchService *instance = nil;
+ (instancetype)shareInstance
{
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
instance = [[SCLaunchService alloc] init] ;
}) ;
return instance ;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
- (instancetype)init{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super init];
});
return instance;
}
- (id)copyWithZone:(NSZone *)zone{
return instance;
}
- (id)mutableCopyWithZone:(NSZone *)zone{
return instance;
}
#pragma mark 開啟服務(wù)
- (void)startSCServices:(VOIDBLOCK)started{
[self configTokenInfo];
started?started():nil;
}
#pragma mark 開啟三方服務(wù)
- (void)openThirdServices:(NSDictionary *)launchOptions block:(VOIDBLOCK)started env:(KEnvType)envType{
[[SCEnvParameterConfig shareInstance] config:envType];
// [LocalCfg config:ENVSTR];
[[SCUser shareInstance]setCacheInfo];
[self addJpushWith:launchOptions];
[self startBuglyService];
[self startWXService];
[self startUMServices];
[self startIQKeyBoard];
[self startRNService];
[[JoyRouter sharedInstance]configScheme:@"scbu"];
started?started():nil;
}
#pragma mark 配置token相關(guān)信息
- (void)configTokenInfo{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshTokenInvalid) name:KTOKEN_REFRESH_FAILURED object:nil];
}
#pragma mark token刷新無效 則跳轉(zhuǎn)登錄
- (void)refreshTokenInvalid{
[[SCBleService shareInstance] stopService];
[[JoyRouter sharedInstance]routerModule:@"user" path:@"login" actionType:JoyRouteActionTypeSetNavRooter parameter:nil block:nil];
}
#pragma mark 極光推送
- (void)addJpushWith:(NSDictionary *)launchOptions{
#if __has_include("JPUSHService.h")
JPUSHRegisterEntity * entity = [[JPUSHRegisterEntity alloc] init];
UIUserNotificationSettings *settings = [UIUserNotificationSettings
settingsForTypes:UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
// [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
entity.types = JPAuthorizationOptionAlert|JPAuthorizationOptionBadge|JPAuthorizationOptionSound;
[JPUSHService registerForRemoteNotificationConfig:entity delegate:self];
NSString *jpushKey = [SCEnvParameterConfig shareInstance].JPUSH_APPKEY;
BOOL isEnvRelease = [SCEnvParameterConfig shareInstance].envType == KEnvTypeRelease;
[JPUSHService setupWithOption:launchOptions appKey:jpushKey
channel:nil
apsForProduction:isEnvRelease
advertisingIdentifier:nil];
[JPUSHService registrationIDCompletionHandler:^(int resCode, NSString *registrationID) {
NSUserDefaults *deaults = [NSUserDefaults standardUserDefaults];
[deaults setObject:registrationID forKey:@"registrationID"];
NSLog(@"resCode : %d,registrationID: %@",resCode,registrationID);
}];
#else
#endif
}
#pragma mark bugly服務(wù)
- (void)startBuglyService{
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
[Bugly startWithAppId:[SCEnvParameterConfig shareInstance].BUGLY_APPID];
BuglyConfig * config = [[BuglyConfig alloc] init];
config.reportLogLevel = BuglyLogLevelWarn;
config.version = version;
[Bugly startWithAppId:[SCEnvParameterConfig shareInstance].BUGLY_APPID config:config];
}
#pragma mark 微信服務(wù)
- (void)startWXService{
#if __has_include("WXApi.h")
[WXApi registerApp:[SCEnvParameterConfig shareInstance].WEIXIN_APPID];
#else
#endif
}
#pragma mark 開啟友盟服務(wù)
- (void)startUMServices{
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
[MobClick startWithConfigure:UMConfigInstance];
[MobClick setAppVersion:version];
NSDictionary *config = @{};
[GLLogging setupWithConfiguration:config];
}
- (void)startIQKeyBoard{
//默認(rèn)為NO
IQKeyboardManager *manager = [IQKeyboardManager sharedManager]; // 獲取類庫的單例變量
manager.enable = YES; // 控制整個功能是否啟用
manager.shouldResignOnTouchOutside = YES;//控制點擊背景是否收起鍵盤
// manager.toolbarManageBehaviour = IQAutoToolbarBySubviews; // 有多個輸入框時雨膨,可以通過點擊Toolbar 上的“前一個”“后一個”按鈕來實現(xiàn)移動到不同的輸入框
manager.enableAutoToolbar = NO; // 控制是否顯示鍵盤上的工具條
// manager.shouldShowTextFieldPlaceholder = NO; // 是否顯示占位文字
// manager.placeholderFont = [UIFont systemFontOfSize:17]; // 設(shè)置占位文字的字體
manager.keyboardDistanceFromTextField = 10.0f; //// 輸入框距離鍵盤的距離
}
#pragma mark jpush delegate
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler {
#if __has_include("JPUSHService.h")
// Required
NSDictionary * userInfo = notification.request.content.userInfo;
NSDictionary *data = [NSDictionary dictionaryWithJsonString:userInfo[@"data"]];
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
[JPUSHService handleRemoteNotification:userInfo];
}
if ([userInfo[@"msgType"] isEqualToString:@"BppPayCallbackMessage"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"PayBcak" object:self userInfo:@{@"orderId":data[@"orderId"],@"status":data[@"status"],@"payType":data[@"payType"]}];
return;
}
if (@available(iOS 10.0, *)) {
if([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]] && [userInfo[@"msgType"] isEqualToString:@"message_center_detail"]) {
NSDictionary *aps = [userInfo objectForKey:@"aps"];
NSString *title = nil;
if ([aps isKindOfClass:NSDictionary.class]){
title = [aps objectForKey:@"alert"];
}
/// iOS10處理遠(yuǎn)程推送
[JPUSHService handleRemoteNotification:userInfo];
/// 應(yīng)用處于前臺收到推送的時候轉(zhuǎn)成本地通知 ===========================
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody =title?:@"告警通知";
notification.userInfo = userInfo;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
return;
}
}
completionHandler(UNNotificationPresentationOptionAlert); // 需要執(zhí)行這個方法擂涛,選擇是否提醒用戶,有Badge聊记、Sound撒妈、Alert三種類型可以選擇設(shè)置
#else
#endif
}
- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {
NSDictionary * userInfo = response.notification.request.content.userInfo;
NSDictionary *data = [NSDictionary dictionaryWithJsonString:userInfo[@"data"]];
//相關(guān)頁面跳轉(zhuǎn)在這個地方實現(xiàn)...
if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
#if __has_include("JPUSHService.h")
[JPUSHService handleRemoteNotification:userInfo];
#else
#endif
}
completionHandler(); // 系統(tǒng)要求執(zhí)行這個方法
}
@end
以上是使用應(yīng)用啟動前這個服務(wù)的大致內(nèi)容。
為了避免這個頁面業(yè)務(wù)太多排监,針對不同的服務(wù)狰右,我們通過分類的形式進(jìn)行擴(kuò)展。
比如AppService(檢查版本更新服務(wù))
#import "SCLaunchService.h"
typedef void (^checkVersionBlock)(NSInteger isNewst); //0不是最新版本 1:是最新版本 3:接口異常
@interface SCLaunchService (AppService)
//版本更新
- (void)checkAppVersion:(checkVersionBlock)block;
@end
#import "SCLaunchService+AppService.h"
#import "SCBUApiRequest+SCAppVersion.h"
#import "SCVersionUpdateView.h"
@implementation SCLaunchService (AppService)
#pragma mark 檢測版本
- (void)checkAppVersion:(checkVersionBlock)block{
//網(wǎng)絡(luò)請求調(diào)服務(wù)端接口 檢測是否需要強更
[SCBUApiRequest checkAppVersion:@{} Success:^(SCBUApiResponse *response) {
NSDictionary *result = response.responseObject;
//獲取本地版本號
NSString *curVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
//獲取服務(wù)端版本號
NSString *serviceVersion = result[@"required_version"];
int currVersionInt = [self convertVersionStrToInt:curVersion];
int serviceVersionInt = [self convertVersionStrToInt:serviceVersion];
if (currVersionInt<serviceVersionInt) {
//required_version 版本號 type更新類型(如果是force 那是就強更社露、非得話是暫不更新和立即更新) result[@"update_content"] 更新說明
SCVersionUpdateView *tipView = [[SCVersionUpdateView alloc] initWithVersion:result[@"required_version"] updateType:result[@"type"] updateCon:result[@"update_content"] updateUrl:result[@"update_url"]];
[tipView show];
block?block(0):nil;
}else{
block?block(1):nil;
}
} Failure:^(NSError *error) {
block?block(2):nil;
}];
}
//版本號轉(zhuǎn)換成整形 把版本號轉(zhuǎn)換成數(shù)值
- (int)convertVersionStrToInt:(NSString *)versionStr{
__block int version = 0;
[[versionStr componentsSeparatedByString:@"."] enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (idx == 0) {
version+= [obj intValue]*1000;
}else if (idx==1){
version+= [obj intValue]*100;
}else if (idx==2){
version+= [obj intValue];
}
}];
return version;
}
@end
接口調(diào)用形式
@implementation SCBUApiRequest (SCAppVersion)
+ (void)checkAppVersion:(NSDictionary *)dic Success:(ApiSuccessBlock)success Failure:(ApiFailureBlock)failure{
NSString *versionCheckUrl = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"sc_version_check"];
if (versionCheckUrl) {
NSString *url = [NSString stringWithFormat:@"%@%@",[SCEnvParameterConfig shareInstance].API_HOST_URL,versionCheckUrl];
[SCBUApiRequest getJsonWithUrl:url param:nil Success:success failure:failure app:SCAppRequestTypeTSP];
}
}
@end