版本更新提示搭建(一)逻辑交互

需求如下:

屏幕快照 2019-03-25 上午11.19.36.png

先说下使用场景和需求:
1)登录时版本更新,用户启动app登录或自动登录时,发送请求需要带本应用的当前版本号,服务返回的结果中包含版本更新信息,并且一般立刻弹出提示让用户选择,我们会分为两种,一种是强制更新(左图)一种是非强制更新。
2)在我的→设置→检查更新 模块中进行版本的更新。

思路:

版本更新一般分为:强制更新和非强制性更新。
强制更新的本质是由服务器来判断,需要拿当前版本号(服务器数据库中存储的应用版本号)和应用发送的版本号相比较。

具体实现如下:

我们项目是组件式开发,因此这部分功能的实现,我们放到了Launch模块组件中。

先创建一个服务单例,其中包含(开启第三方、登录成功之后数据初始化、Jpush、Bugly、WX、UM、IQ、RN、配置token相关信息、token刷新无效 则跳转登录)等相关服务。

本篇文章只针对检查版本更新做介绍。

在程序开始启动的时候,增加如下代码

- (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;

/**
  开启服务(登录成功后数据初始化)
 */
- (void)startSCServices:(VOIDBLOCK)started;

/**
 开启三方服务
 @param started started description
 */
- (void)openThirdServices:(NSDictionary *)launchOptions block:(VOIDBLOCK)started env:(KEnvType)envType;
@interface SCLaunchService ()<JPUSHRegisterDelegate,JoyRouteProtocol>
@end
@implementation SCLaunchService
//用于登录页面完成之后相关服务的开启
-(void)routeParam:(NSDictionary *)param block:(JoyRouteBlock)block{
    if ([[param objectForKey:@"type"] isEqualToString:@"startSCServices"]) {
        [self startSCServices:^{
            block?block(param,nil):nil;
        }];
    }
}

//创建一个单例
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 开启服务
- (void)startSCServices:(VOIDBLOCK)started{
    [self configTokenInfo];
    started?started():nil;
}

#pragma mark 开启三方服务
- (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相关信息
- (void)configTokenInfo{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshTokenInvalid) name:KTOKEN_REFRESH_FAILURED object:nil];
}

#pragma mark token刷新无效 则跳转登录
- (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服务
- (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 微信服务
- (void)startWXService{
#if __has_include("WXApi.h")
    [WXApi registerApp:[SCEnvParameterConfig shareInstance].WEIXIN_APPID];
#else
#endif
}

#pragma mark 开启友盟服务
- (void)startUMServices{
    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
    [MobClick startWithConfigure:UMConfigInstance];
    [MobClick setAppVersion:version];
    NSDictionary *config = @{};
    [GLLogging setupWithConfiguration:config];
}

- (void)startIQKeyBoard{
    //默认为NO
    IQKeyboardManager *manager = [IQKeyboardManager sharedManager]; // 获取类库的单例变量
    manager.enable = YES; // 控制整个功能是否启用
    manager.shouldResignOnTouchOutside = YES;//控制点击背景是否收起键盘
    //    manager.toolbarManageBehaviour = IQAutoToolbarBySubviews;   // 有多个输入框时,可以通过点击Toolbar 上的“前一个”“后一个”按钮来实现移动到不同的输入框
    manager.enableAutoToolbar = NO; // 控制是否显示键盘上的工具条
    //    manager.shouldShowTextFieldPlaceholder = NO;  // 是否显示占位文字
    //    manager.placeholderFont = [UIFont systemFontOfSize:17]; // 设置占位文字的字体
    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处理远程推送
            [JPUSHService handleRemoteNotification:userInfo];
            /// 应用处于前台收到推送的时候转成本地通知 ===========================
            UILocalNotification *notification = [[UILocalNotification alloc] init];
            notification.alertBody =title?:@"告警通知";
            notification.userInfo = userInfo;
            [[UIApplication sharedApplication] presentLocalNotificationNow:notification];
            return;
        }
    }
    completionHandler(UNNotificationPresentationOptionAlert); // 需要执行这个方法,选择是否提醒用户,有Badge、Sound、Alert三种类型可以选择设置
#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"]];
//相关页面跳转在这个地方实现...
    
    if([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
#if __has_include("JPUSHService.h")
        [JPUSHService handleRemoteNotification:userInfo];
#else
#endif
    }
    completionHandler();  // 系统要求执行这个方法
}

@end

以上是使用应用启动前这个服务的大致内容。

为了避免这个页面业务太多,针对不同的服务,我们通过分类的形式进行扩展。
比如AppService(检查版本更新服务)

#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{
    //网络请求调服务端接口 检测是否需要强更
    [SCBUApiRequest checkAppVersion:@{} Success:^(SCBUApiResponse *response) {
        NSDictionary *result = response.responseObject;
//获取本地版本号
        NSString *curVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
//获取服务端版本号
        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;
    }];
}

//版本号转换成整形  把版本号转换成数值
- (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

接口调用形式

@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
屏幕快照 2019-03-25 下午3.33.49.png

弹出发现新版本View具体实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335