iOS-OC-APP热更新,动态更新(仿QQ打开或关闭某个功能)
一.前言
二.创建Framework
1.新建项目
- <span style="font-size:18px;">- (void)uiConfig{
- self.title = @"这是功能2";
- UIImageView *imageView = [[UIImageView alloc]init];
- imageView.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight);
- NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img4.duitang.com/uploads/item/201405/31/20140531174207_hH5u4.thumb.700_0.jpeg"]];
- imageView.image = [UIImage imageWithData:data];
- [self.view addSubview:imageView];
- UILabel *label = [[UILabel alloc]init];
- label.backgroundColor = [UIColor clearColor];
- label.frame = CGRectMake(0, (ScreenHeight - 100)/2, ScreenWidth, 100);
- label.numberOfLines = 0;
- label.text = @"这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2这是功能2";
- [self.view addSubview:label];
- }</span>
2.添加Aggregate
3.添加Run Script脚本
4.脚本源码
- <span style="font-size:18px;"># Sets the target folders and the final framework product.
- # 如果工程名称和Framework的Target名称不一样的话,要自定义FMKNAME
- # 例如: FMK_NAME = "MyFramework"
- FMK_NAME=${PROJECT_NAME}
- # Install dir will be the final output to the framework.
- # The following line create it in the root folder of the current project.
- INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
- # Working dir will be deleted after the framework creation.
- WRK_DIR=build
- DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
- SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
- # -configuration ${CONFIGURATION}
- # Clean and Building both architectures.
- xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build
- xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build
- # Cleaning the oldest.
- if [ -d "${INSTALL_DIR}" ]
- then
- rm -rf "${INSTALL_DIR}"
- fi
- mkdir -p "${INSTALL_DIR}"
- cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
- # Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
- lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
- rm -r "${WRK_DIR}"
- open "${INSTALL_DIR}"
- </span>
5.运行打包
三.创建项目
1.创建功能列表数据
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
- // Override point for customization after application launch.
- self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
- self.window.backgroundColor = [UIColor whiteColor];
- [self.window makeKeyAndVisible];
- //添加假的功能列表
- NSArray *functionList = [USER_DEFAULT objectForKey:@"functionList"];
- if(functionList==nil || functionList.count==0){
- NSArray *titleArr = @[@"功能1",@"功能2",@"功能3",@"功能4"];
- NSArray *className = @[@"HotUpdateControl",@"ZFJViewController",@"",@""];
- NSArray *classType = @[@"NSObject",@"UIViewController",@"",@""];
- NSArray *downUrl = @[
- @"http://7xqdun.com1.z0.glb.clouddn.com/HotMudel.framework.zip",
- @"http://7xqdun.com1.z0.glb.clouddn.com/FunctionZFJ1.framework.zip",
- @"",
- @""];
- NSMutableArray *functionArr = [[NSMutableArray alloc]init];
- for (int i = 0; i<titleArr.count; i++) {
- NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
- [dict setObject:titleArr[i] forKey:@"name"];
- [dict setObject:className[i] forKey:@"classname"];
- [dict setObject:classType[i] forKey:@"classtype"];
- [dict setObject:@(i) forKey:@"mid"];
- [dict setObject:@"0" forKey:@"isopen"];//0 未开启 1开启了
- [dict setObject:downUrl[i] forKey:@"downurl"];
- [functionArr addObject:dict];
- }
- [USER_DEFAULT setObject:functionArr forKey:@"functionList"];
- [USER_DEFAULT synchronize];
- }
- DynamicViewController *dvc = [[DynamicViewController alloc]init];
- UINavigationController *nvc = [[UINavigationController alloc]initWithRootViewController:dvc];
- self.window.rootViewController = nvc;
- return YES;
- }
2.展示功能列表
- - (void)getDataBase{
- [self.dataArray removeAllObjects];
- NSArray *functionList = [USER_DEFAULT objectForKey:@"functionList"];
- for (NSDictionary *dict in functionList) {
- NSInteger isopen = [dict[@"isopen"] integerValue];
- if(isopen==1){
- [self.dataArray addObject:dict];
- }
- }
- [self.tableview reloadData];
- }
b.点击对于的tableviewcell 的时候跳转对应的framework读取出来的方法
- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
- [tableView deselectRowAtIndexPath:indexPath animated:YES];
- NSDictionary *dict = self.dataArray[indexPath.row];
- //获取framework的路径名,我已mid区分
- NSString *destinationPath = [NSHomeDirectory() stringByAppendingString:[NSString stringWithFormat:@"/Documents/FunctionZFJ%@",dict[@"mid"]]];
- NSArray* arrFramework = [self getFilenamelistOfType:@"framework" fromDirPath:destinationPath];
- NSString *bundlePath = [NSString stringWithFormat:@"%@/%@",destinationPath,[arrFramework lastObject]];
- if (![[NSFileManager defaultManager] fileExistsAtPath:bundlePath]) {
- NSLog(@"文件不存在");
- return;
- }
- NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
- if (!bundle || ![bundle load]) {
- NSLog(@"bundle加载出错");
- }
- NSString *className = dict[@"classname"];
- NSString *classtype = dict[@"classtype"];
- Class loadClass = [bundle classNamed:className];
- if (!loadClass) {
- NSLog(@"获取失败");
- return;
- }
- if([classtype isEqualToString:@"NSObject"]){
- NSObject *bundleObj = [loadClass new];
- NSArray *arrVc = [bundleObj performSelector:@selector(getVcs)];
- TabController *tvc = [[TabController alloc]initwithVcArray:arrVc];
- [self.navigationController pushViewController:tvc animated:YES];
- }else if([classtype isEqualToString:@"UIViewController"]){
- UIViewController *uvc = (UIViewController *)[loadClass new];
- [self.navigationController pushViewController:uvc animated:YES];
- }
- }
c.效果图
3.更多功能
- <span style="font-size:18px;">#pragma mark - 获取全部数据
- - (void)getDataBase{
- [self.dataArray removeAllObjects];
- NSArray *functionList = [USER_DEFAULT objectForKey:@"functionList"];
- NSMutableArray *openYES = [[NSMutableArray alloc]init];
- NSMutableArray *openNO = [[NSMutableArray alloc]init];
- for (NSDictionary *dict in functionList) {
- NSMutableDictionary *muDict = [[NSMutableDictionary alloc]initWithDictionary:dict];
- NSInteger isopen = [muDict[@"isopen"] integerValue];
- if(isopen==1){
- //已经打开的功能
- [openYES addObject:muDict];
- }else{
- //没有打开的功能
- [openNO addObject:muDict];
- }
- }
- [self.dataArray addObject:openNO];
- [self.dataArray addObject:openYES];
- [self.tableview reloadData];
- }</span>
b.打开功能
- <span style="font-size:18px;">#pragma mark - 开启某个功能 先下载数据
- - (void)SSZipArchiveDataBaseWithDict:(NSMutableDictionary *)dict{
- NSString *requestURL = dict[@"downurl"];
- if(requestURL==nil || requestURL.length==0){
- self.progresslabel.text = [NSString stringWithFormat:@"%@-没有下载地址,不能开启!",dict[@"name"]];
- UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"没有下载地址,不能开启" preferredStyle:UIAlertControllerStyleAlert];
- UIAlertAction *sureBtn = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
- [alertController addAction:sureBtn];
- [self presentViewController:alertController animated:YES completion:nil];
- return;
- }
- //下载保存的路径
- NSString *savedPath = [NSHomeDirectory() stringByAppendingString:[NSString stringWithFormat:@"/Documents/FunctionZFJ%@.framework.zip",dict[@"mid"]]];
- AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
- NSMutableURLRequest *request = [serializer requestWithMethod:@"POST" URLString:requestURL parameters:nil error:nil];
- AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:request];
- [operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:savedPath append:NO]];
- [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
- float progress = (float)totalBytesRead / totalBytesExpectedToRead;
- self.progresslabel.text = [NSString stringWithFormat:@"%@下载进度:%.2f",dict[@"name"],progress];
- }];
- [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
- NSLog(@"下载成功");
- NSString *destinationPath = [NSHomeDirectory() stringByAppendingString:[NSString stringWithFormat:@"/Documents/FunctionZFJ%@",dict[@"mid"]]];
- //对下载下来的ZIP包进行解压
- BOOL isScu = [SSZipArchive unzipFileAtPath:savedPath toDestination:destinationPath];
- if(isScu){
- NSLog(@"解压成功");
- NSFileManager *fileMgr = [NSFileManager defaultManager];
- BOOL bRet = [fileMgr fileExistsAtPath:savedPath];
- if (bRet) {
- [fileMgr removeItemAtPath:savedPath error:nil];//解压成功后删除压缩包
- }
- [dict setValue:@"1" forKey:@"isopen"];
- [self updataBaseWithDict:dict];//解压成功后更新本地功能列表状态
- }else{
- NSLog(@"解压失败 --- 开启失败");
- }
- } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
- NSLog(@"下载失败 --- 开启失败");
- }];
- [operation start];
- }
- </span>
更新本地数据
- <span style="font-size:18px;">#pragma mark - 更新本地数据
- - (void)updataBaseWithDict:(NSMutableDictionary *)dict{
- NSInteger mid = [dict[@"mid"] integerValue];
- NSMutableArray *functionList = [USER_DEFAULT objectForKey:@"functionList"];
- NSMutableArray *dataArr = [[NSMutableArray alloc]initWithArray:functionList];
- [dataArr replaceObjectAtIndex:mid withObject:dict];
- [USER_DEFAULT setObject:dataArr forKey:@"functionList"];
- BOOL isScu = [USER_DEFAULT synchronize];
- if(isScu){
- [self getDataBase];//重新获取数据 更新列表
- if(self.refreshData){
- self.refreshData();
- }
- }else{
- NSLog(@"c操作失败");
- }
- }
- </span>
- <span style="font-size:18px;">#pragma mark - 关闭某个功能
- - (void)delectFunctionZFJWithDict:(NSMutableDictionary *)dict{
- NSFileManager *fileMgr = [NSFileManager defaultManager];
- NSString *savedPath = [NSHomeDirectory() stringByAppendingString:[NSString stringWithFormat:@"/Documents/FunctionZFJ%@",dict[@"mid"]]];
- BOOL bRet = [fileMgr fileExistsAtPath:savedPath];
- if (bRet) {
- NSError *err;
- //关闭某个功能 就是删除本地的framework 然后修改本地功能状态
- BOOL isScu = [fileMgr removeItemAtPath:savedPath error:&err];
- if(isScu){
- [dict setValue:@"0" forKey:@"isopen"];
- [self updataBaseWithDict:dict];
- }else{
- NSLog(@"关闭失败");
- }
- }else{
- NSLog(@"关闭失败");
- }
- }
- </span>
四.源代码
五.效果图
iOS-OC-APP热更新,动态更新(仿QQ打开或关闭某个功能)的更多相关文章
- App热补丁动态修复技术介绍
安卓App热补丁动态修复技术介绍 来自qq空间团队:微信号qzonemobiledev QQ空间终端开发团队 1.背景 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就 ...
- iOS传感器集锦、飞机大战、开发调试工具、强制更新、Swift仿QQ空间头部等源码
iOS精选源码 飞机大作战 MUPhotoPreview -简单易用的图片浏览器 LLDebugTool是一款针对开发者和测试者的调试工具,它可以帮... 多个UIScrollView.UITable ...
- 安卓App热补丁动态修复技术介绍
版权声明:本文由johncz原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/169 来源:腾云阁 https://www.q ...
- Android仿qq聊天记录长按删除功能效果
最近项目在做IM即时通讯开发,在删除聊天列表的时候跟删除聊天详细信息的时候,产品经理想要跟ios一样,在当前选中行上方弹出一个删除窗口.于是先从网上找demo,找了一个发现是Dialog做的,我感觉没 ...
- js高仿QQ消息列表左滑功能
该组件,主要功能类似于QQ消息列表左滑出现删除.标为已读等按钮的功能:现在的版本用的是纯javaScript编写:后续会跟进 angularJs 开发的类似组件以及jquery的; 下面,就让我们来认 ...
- 移动端APP热更新方案(iOS+Android)
出自:http://www.cnblogs.com/Creator/p/7007694.html 为什么要做热更新 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙 ...
- APP热更新方案
为什么要做热更新 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就会忙得焦头烂额:重新打包App.测试.向各个应用市场和渠道换包.提示用户升级.用户下载.覆盖安装. 重 ...
- APP热更新方案(转)
本文转载自[http://creator.cnblogs.com/] 博客地址:Zealot Yin 为什么要做热更新 当一个App发布之后,突然发现了一个严重bug需要进行紧急修复,这时候公司各方就 ...
- android Qzone的App热补丁热修复技术
转自:https://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=400118620&idx=1&sn=b4fdd5055731 ...
随机推荐
- python profile
一.profile,cProfile 1. python -m cProfile myprogram.py python -m profile myprog.py2. 使用import profile ...
- perl小记
perl是一种运行式脚本,所以在debug的时候,多用print,在后台看有没有输出相关的结果,来判断相应的错误. 部分perl正则表达式: $gene =~/^LOC/ 以“LOC”开头的字符串 ...
- [linux] 更改目录显示颜色
第一.创建配置文件 $dircolors -p>~/.dircolors 第二.编辑配置文件 打开文件: $vi ~/.dircolors 找到这一行: DIR ; # directory #可 ...
- Servlet练习
编写一个Servlet,注册登录成功后,讲表单中的内容输出到页面当中 <%@ page language="java" contentType="text/html ...
- C# 基础(5)--字符串
Params 可变参数,只能修饰数组,可以传递数组,也可以传递数组的元素. 要抛弃一个异常,可以这样写: Throe new exeception?? 命名空间 不在同一个命名空间下的类,不同直接访问 ...
- (转)windows下安装nodejs及框架express
转自:http://jingyan.baidu.com/article/456c463b60fb380a583144a9.html windows下安装nodejs及框架express nodejs从 ...
- Linux内核分析之跟踪分析Linux内核的启动过程
一.实验过程 使用实验楼虚拟机打开shell cd LinuxKernel/ qemu -kernel linux-/arch/x86/boot/bzImage -initrd rootfs.img ...
- dubbo demo实现
粗略的写了一个dubbo的demo,使用了alibaba的dubbo,还有zookeeper来做配置中心 参考资料地址: http://dubbo.io/User+Guide-zh.htm#UserG ...
- Neither BindingResult nor plain target object for bean
当你开发一个项目,如果你选择的是spring MVC 框架,而你在前台使用spring的标签时,那么你有可能出现在这个情况. javax.servlet.jsp.JspTagException: Ne ...
- day4----生成器,迭代器
迭代器,生成器,装饰器 1.生成器 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要 ...