iOS-OC-APP熱更新,動態(tài)更新(仿QQ打開或關閉某個功能)

一.前言
iOS開發(fā)更新APP我覺得是比較坑的就是審核時間比較長吗坚,審核比較嚴仔粥,對于剛入行的小伙伴來說示姿,雷區(qū)比較多筹误;所以熱更新是比較重要的桐早;
大家也許會發(fā)現(xiàn)我們常用的QQ現(xiàn)在下來也就一百多兆,但是用了幾個月后發(fā)現(xiàn)QQ在手機上占有一個多G的內(nèi)存厨剪,特別是手機內(nèi)存比較小的小伙伴哄酝,這是因為你在使用過程中,有一些功能是你下載下來的祷膳;


二.創(chuàng)建Framework
1.新建項目
新建一個Cocoa Touch Framework項目陶衅,然后在這個項目里面寫你的新的功能,比如我創(chuàng)建了一個控制器直晨,在控制器里面加載一張圖和一個label搀军;
<pre>- (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];
}</pre>
2.添加Aggregate

在TARGETS里面新建一個Aggregate



3.添加Run Script腳本



4.腳本源碼
<pre>

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}" </pre>
5.運行打包

運行工程抡秆,將生成的framework包壓縮zip奕巍,然后上傳服務器;
例如:http://7xqdun.com1.z0.glb.clouddn.com/FunctionZFJ1.framework.zip

三.創(chuàng)建項目
在項目中我們主要是下載和讀取framework包儒士;我們先要獲取功能列表的止,在此我在本地寫了一個功能列表,大家如果用得到可以將功能列表存放在服務器上着撩;
1.創(chuàng)建功能列表數(shù)據(jù)
我添加了四個功能模塊诅福,存在NSUserDefaults里面;其中功能1和功能2有下載地址拖叙,其他的沒有氓润;功能1是個NSObject,功能2直接是一個控制器薯鳍;
isopen:1表示打開咖气,0表示關閉;
<pre>- (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;
} </pre>
2.展示功能列表
在功能列表主要用于展示所有打開過的功能,也就是isopen為1的所有功能崩溪;
a.獲取本地所有打開的數(shù)據(jù)浅役,然后在tableview上顯示
<pre>- (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];
}
</pre>

b.點擊對于的tableviewcell 的時候跳轉對應的framework讀取出來的方法
注意,我將不同的framework存放在不同的文件夾下伶唯,以mid作為區(qū)分觉既;

<pre>- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *dict = self.dataArray[indexPath.row];
//獲取framework的路徑名,我已mid區(qū)分
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];
}
} </pre>
c.效果圖

3.更多功能
在這里我們可以打開或者關閉某個功能乳幸;
a.獲取所以功能瞪讼,包括打開或者關閉狀態(tài)的;然后在tableview上顯示粹断;
<pre>

pragma mark - 獲取全部數(shù)據(jù)

  • (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){
    //已經(jīng)打開的功能
    [openYES addObject:muDict];
    }else{
    //沒有打開的功能
    [openNO addObject:muDict];
    }
    }

    [self.dataArray addObject:openNO];
    [self.dataArray addObject:openYES];

    [self.tableview reloadData];
    }
    </pre>

b.打開功能
打開某個功能就是下載對應的framework符欠,把下載下來的zip包進行解壓一下然后獲取到framework,接著刪除zip包瓶埋,把framework放在對于的目錄下背亥;最后改變本地列表功能的狀態(tài);
<pre>#pragma mark - 開啟某個功能 先下載數(shù)據(jù)
-(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];//解壓成功后更新本地功能列表狀態(tài)
}else{
NSLog(@"解壓失敗 --- 開啟失敗");
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"下載失敗 --- 開啟失敗");
}];
[operation start];
}
</pre>
更新本地數(shù)據(jù)
<pre>#pragma mark - 更新本地數(shù)據(jù)
-(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];//重新獲取數(shù)據(jù) 更新列表
if(self.refreshData){
self.refreshData();
}
}else{
NSLog(@"c操作失敗");
}
} </pre>
c.關閉功能
關閉某個功能狡汉,也就是刪除某個功能的framework,然后更改功能列表的狀態(tài)闽颇;
<pre>#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 然后修改本地功能狀態(tài)
BOOL isScu = [fileMgr removeItemAtPath:savedPath error:&err];
if(isScu){
[dict setValue:@"0" forKey:@"isopen"];
[self updataBaseWithDict:dict];
}else{
NSLog(@"關閉失敗");
}
}else{
NSLog(@"關閉失敗");
}
} </pre>
d.效果圖


四.源代碼

在這里面有盾戴,兩個framework的源代碼,可項目的代碼兵多;
注意尖啡,如果有多個功能的framework,記住多個framework的命名在同一個功能里面不能重復剩膘,不然調取失斝普丁;
鏈接: http://download.csdn.net/detail/u014220518/9643039

五.效果圖



轉載請注明來源:http://blog.csdn.net/u014220518/article/details/52248803

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怠褐,一起剝皮案震驚了整個濱河市畏梆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奈懒,老刑警劉巖奠涌,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異磷杏,居然都是意外死亡溜畅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門极祸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慈格,“玉大人怠晴,你說我怎么就攤上這事≡±Γ” “怎么了龄寞?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長汤功。 經(jīng)常有香客問我,道長溜哮,這世上最難降的妖魔是什么滔金? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮茂嗓,結果婚禮上餐茵,老公的妹妹穿的比我還像新娘。我一直安慰自己述吸,他們只是感情好忿族,可當我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蝌矛,像睡著了一般道批。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上入撒,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天隆豹,我揣著相機與錄音,去河邊找鬼茅逮。 笑死璃赡,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的献雅。 我是一名探鬼主播碉考,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挺身!你這毒婦竟也來了侯谁?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤章钾,失蹤者是張志新(化名)和其女友劉穎良蒸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伍玖,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡嫩痰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了窍箍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片串纺。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡丽旅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纺棺,到底是詐尸還是另有隱情榄笙,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布祷蝌,位于F島的核電站茅撞,受9級特大地震影響,放射性物質發(fā)生泄漏巨朦。R本人自食惡果不足惜米丘,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望糊啡。 院中可真熱鬧拄查,春花似錦、人聲如沸棚蓄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梭依。三九已至稍算,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間役拴,已是汗流浹背邪蛔。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留扎狱,地道東北人侧到。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像淤击,于是被迫代替她去往敵國和親匠抗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,795評論 25 707
  • Swift版本點擊這里歡迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh閱讀 25,334評論 7 249
  • 我的同事我的戰(zhàn)友矢腻,雞年的到來,將把我這個水利局的大姐大送入退休的候車室射赛,在這里等候退休的列車多柑。 沒有了事業(yè)的...
    含羞姑閱讀 360評論 0 0
  • “娜娜?”憶言言一回頭楣责,就看見了躲在櫻花樹后面瑟瑟發(fā)抖的尹慕娜竣灌,“你怎么了聂沙?為什么在這里?” “我……我……我什么...
    慕櫻吶閱讀 260評論 0 1
  • 小貝超喜歡小動物初嘹,小狗及汉,小貓,小鳥屯烦,小兔子坷随,這些毛茸茸的家伙們她都喜歡。就連小狗豆包咬了她后驻龟,還是沒有任何芥蒂的跟...
    白色冰菊閱讀 438評論 0 4