JSPatch來更新已上線的App中出現的BUG(超級詳細)
JSPatch的作用是什么呢?
簡單來說:(后面有具體的操作步驟以及在操作過程中會出現的錯誤)
1.iOS應用程序上架到AppStore需要等待蘋果公司的審核,一般審核時間需要1到2周.雖然程序在上架前會經過測試人員的測試,但有時候還是不免會發生新版本上線后出現嚴重的bug,導致用戶剛升級到新版本就出現crash,嚴重影響用戶體驗.
2.這時能做的只是趕緊修復bug然后提交等待漫長的AppStore審核,再盼望用戶快點升級,才能完成此次bug的修復,這樣及容易導致用戶流失,這對企業來說影響非常嚴重.
3.為了解決由於AppStore審核而導致程序更新新版本慢,目前有以下2種方案實時修復線上bug:
1⃣️直接使用UIWebView加載網絡上的HTML的代碼,這樣如果有問題只需要更新服務器的HTML文件,用戶重新進入程序,加載新的HTML文件,整個程序就能更新.對用戶影響非常小
2⃣️使用其他腳本語言通過Runtime動態調用OC
1⃣️.1⃣️WaxPatch:它把Lua腳本語言和原生Objective-C應用編程接口(API)結合起來,通過Lua腳本來調用OC
1⃣️.2⃣️JSPatch:JS是通過JavaScriptCore.framework調用Runtime,來實現JS調用OC
JSPatch優勢:
1⃣️JS比Lua在應用開發領域有更廣泛的應用,目前前端開發和前端開發有融合的趨勢,作為擴展的腳本語言,JS是不二之選
2⃣️JSPatch更符合Apple的規則。iOS Developer Program License Agreement里3.3.2提到不可動態下發可執行代碼,但通過蘋果JavaScriptCore.framework或WebKit執行的代碼除外,JS正是通過JavaScriptCore.framework執行的
3⃣️使用系統內置的JavaScriptCore.framework,無需內嵌腳本引擎,體積小巧
4⃣️支持block
JSPatch缺點:
1⃣️相對於WaxPatch,JSPatch劣勢在於不支持iOS6,因為需要引入JavaScriptCore.framework
2⃣️另外目前內存的使用上會高於wax,持續改進中
存在風險:JSPatch讓腳本語言獲得調用所有原生OC方法的能力,不像web前端把能力局限在瀏覽器,使用上會有一些安全風險
1⃣️若在網絡傳輸過程中下發明文JS,可能會被中間人篡改JS腳本,執行任意方法,盜取APP里的相關信息,危及用戶信息和APP
2⃣️若下載完后的JS保存在本地沒有加密,在越獄的機器上用戶也可以手動替換或篡改腳本
風險控制:
1⃣️JSPatch腳本的執行權限很高,若在傳輸過程中被中間人篡改,會帶來很大的安全問題,為了防止這種情況出現,在傳輸過程中對JS文件進行了RSA簽名加密,流程如下:
服務端:計算JS文件MD5值。用RSA私鑰對MD5值進行加密,與JS文件一起下發給客戶端。
客戶端:拿到加密數據,用RSA公鑰解密出MD5值。本地計算返回的JS文件MD5值。對比上述的兩個MD5值,若相等則校驗通過,取JS文件保存到本地。
由於RSA是非對稱加密,在沒有私鑰的情況下第三方無法加密對應的MD5值,也就無法偽造JS文件,杜絕了JS文件在傳輸過程被篡改的可能。
2⃣️本地存儲
本地存儲的腳本被篡改的機會小很多,只在越獄機器上有點風險,對此JSPatch SDK在下載完腳本保存到本地時也進行了簡單的對稱加密,每次讀取時解密。
JSPatch需要使用者有一個后台可以下發和管理腳本,並且需要處理傳輸安全等部署工作,JSPatch平台幫你做了這些事,提供了腳本后台托管,版本管理,保證傳輸安全等功能,讓你無需搭建一個后台,無需關心部署操作,只需引入一個SDK即可立即使用JSPatch。
JSPatch平台速度和穩定性如何?
通過JSPatch平台上傳的腳本文件都會保存在七牛雲存儲上,客戶端APP只跟七牛服務器通訊,支持高並發,CDN分布全國,速度和穩定性有保證。
除了修復bug,JSPatch也可以用於動態運營,實時修改線上APP行為,或動態添加功能。
JSPatch詳細使用文檔見Github Wiki
第一步:創建好自己的項目
#import"ViewController.h"
@interfaceViewController()
@property(nonatomic,weak)UITableView*table;
//創建可變數組來更新數據
@property(nonatomic,strong)NSMutableArray*dataArray;
@end
@implementationViewController
#pragma mark -懶加載可變數組
- (NSMutableArray*)dataArray
{
if(_dataArray==nil)
{
_dataArray= [NSMutableArrayarray];
[_dataArrayaddObjectsFromArray:@[@"huangfang--1",@"huangfang--2",@"huangfang--3",@"huangfang--4",@"huangfang--5"]];
}
return_dataArray;
}
- (void)viewDidLoad
{
[superviewDidLoad];
self.view.backgroundColor= [UIColorgrayColor];
[selfsetUpVCWithContent];
}
#pragma mark -設置控制器的內容
-(void)setUpVCWithContent
{
//自定義導航欄的標題控件
UILabel*label = [[UILabelalloc]initWithFrame:CGRectMake(0,0,100,44)];
label.text=@"HFJSpatch";
label.textColor= [UIColorgreenColor];
label.font= [UIFontboldSystemFontOfSize:30];
self.navigationItem.titleView= label;
//自定義導航欄右邊的item
UIButton*button =[UIButtonbuttonWithType:UIButtonTypeContactAdd];
[buttonaddTarget:selfaction:@selector(addData)forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem*rightButton = [[UIBarButtonItemalloc]initWithCustomView:button];
self.navigationItem.rightBarButtonItem= rightButton;
UITableView*table = [[UITableViewalloc]initWithFrame:self.view.bounds];
self.table= table;
table.dataSource=self;
[self.viewaddSubview:table];
}
#pragma mark -添加數據的方法
-(void)addData
{
NSString*datas =nil;
[self.dataArrayaddObject:datas];
[self.tablereloadData];
}
#pragma mark -數據源方法
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
returnself.dataArray.count;
}
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
staticNSString*ID =@"HFcell";
UITableViewCell*cell = [tableViewdequeueReusableCellWithIdentifier:ID];
if(cell ==nil)
{
cell = [[UITableViewCellalloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:ID];
cell.textLabel.textColor= [UIColorredColor];
cell.textLabel.textAlignment=NSTextAlignmentCenter;
cell.textLabel.font= [UIFontsystemFontOfSize:20];
if(indexPath.row>5)
{
cell.textLabel.textColor= [UIColororangeColor];
cell.textLabel.textAlignment=NSTextAlignmentRight;
cell.textLabel.font= [UIFontboldSystemFontOfSize:25];
}
}
cell.textLabel.text=self.dataArray[indexPath.row];
returncell;
}
@end
第二步:登錄http://jspatch.com該網站,這里需要注冊一下賬號哈(已有賬號的話,可以直接登錄)

下載SDK到本地

第三步:下載完成后,打開我們已經創建好的項目,將下載好的SDK拖到我們的工程中.

第四步:需要在我們的工程中添加兩個框架( libz.tbd 和 JavaScriptCore.framework )




第五步:在項目工程里先創建一個空的文件(main.js),這個就不用說了吧,大家都會哈^_^
注:如果想在空文件中顯示JavaScript的格式的話,可以進行一下操作:
1、先選中剛才創建好的(main.js)

2.

3.在文件中寫入代碼
//main.js
// 指定要更新的對應的控制器
defineClass("ViewController", {
// 添加或修改的方法(
addData: function() {
// 獲取到控制器中的可變數組
var datas = self.dataArray();
datas.addObject("huangfang6");
datas.addObject("huangfang7");
datas.addObject("huangfang8");
datas.addObject("huangfang9");
// 如果添加成功會將數組中的第一個元素打印出來(這個可以根據需要進行打印)
// console.log(datas.firstObject());
// 如果添加成功會將數組中的第一個元素打印出來
console.log(datas.lastObject());
self.table().reloadData();
}
})
注:上面的 addData: function() 中的addData要和我們項目中的點擊按鈕執行的方法一樣,如果不一樣會報錯的
4、在我們的項目工程中找到AppDelegate.m文件,在以下方法中添加一行測試的代碼,我們先在本地測試一下,看看是否成功
#import “AppDelegate.h"
#import “ViewController.h"
#import<JSPatch/JSPatch.h>
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
ViewController *vc = [[ViewController alloc]init];
vc.view.backgroundColor = [UIColor magentaColor];
UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:vc];
self.window.rootViewController = nav;
[self.window makeKeyWindow];
[JSPatch testScriptInBundle];
return YES;
}
@end
以下為運行結果 點擊+后出現右對齊的三個

第六步:接下來就需要將我們的main.js文件添加到服務器上來進行操作:
1、將我們的項目工程中的main.js文件項目中移除,在移除之前要復制一份出來,方便等會將main.js 文件上傳到服務器哈

2、登錄到之前登錄的網站,執行以下操作:
2.1 添加我們要更新的項目名稱:XGJSPatch

2.2 點擊新增APP后,出現如下界面,填寫完后,直接點擊"提交"按鈕就可以了

2.3 接下來會看到以下界面:


2.4 點擊上圖中的”添加APP版本",進入如下界面


點擊進入下一個界面




第七步:在appDelegate.m文件中寫入下面的代碼:

一切准備就緒后,可以直接運行 可能會出現以下幾個錯誤

ATS:安全機制 在info.plist中加如下代碼



如果都沒有錯誤的話,運行成功后的界面如下圖:


下圖是我重新更改了下數據 再重新添加了補丁 運行出來的結果
