JSPatch 是什么
JSPatch 是一個開源項目(Github鏈接),只需要在項目里引入極小的引擎文件,就可以使用 JavaScript 調用任何 Objective-C 的原生接口父叙,替換任意 Objective-C 原生方法。目前主要用于下發(fā) JS 腳本替換原生 Objective-C 代碼练俐,實時修復線上 bug注暗。
JSPatch平臺又是什么鬼
JSPatch需要你自己搞一個服務器管理、下發(fā)腳本猫牡,還要處理安全問題胡诗,高并發(fā)問題,煩死你淌友。JSPatch平臺封裝了SDK煌恢,你只需要繼承SDK就可以省去一堆的麻煩。
JSPatch 需要使用者有一個后臺可以下發(fā)和管理腳本震庭,并且需要處理傳輸安全等部署工作瑰抵,JSPatch 平臺幫你做了這些事,提供了腳本后臺托管器联,版本管理二汛,保證傳輸安全等功能婿崭,讓你無需搭建一個后臺,無需關心部署操作肴颊,只需引入一個 SDK 即可立即使用 JSPatch氓栈。
JSPatch 平臺的 SDK 在核心代碼的基礎上增加了向平臺請求腳本/傳輸解密/版本管理等功能,只用于這個平臺婿着。
通過 JSPatch 平臺上傳的腳本文件都會保存在七牛云存儲上授瘦,客戶端 APP 只跟七牛服務器通訊,支持高并發(fā)竟宋,CDN分布全國提完,速度和穩(wěn)定性有保證。
SDK接入
這種問題不要問我袜硫,注冊一個賬號氯葬,建一個app獲取到appid,然后接入SDK完事兒婉陷。至于你是用cocoapods還是手動接入帚称,全憑個人喜好。SDK接入
API主要方法
+startWithAppKey:
傳入在平臺申請的 appKey秽澳,啟動 JSPatch SDK闯睹。在-application:didFinishLaunchingWithOptions:
開頭初始化第三方庫的時候一并調用初始化。
+sync
與 JSPatch 平臺后臺同步担神,詢問是否有 patch 更新楼吃,如果有更新會自動下載并執(zhí)行。
每調用一次 +sync 就會請求一次后臺妄讯,如果app啟動的時候檢查一次就OK的話就在
-application:didFinishLaunchingWithOptions:
調用一次孩锡。如果實時性要求高,就在
-applicationDidBecomeActive:
的時候調用亥贸。
+setupLogger:
SDK打一些請求和執(zhí)行的log躬窜,默認是NSLog()
輸出,如果app有自己的日志系統(tǒng)炕置,并且希望自己的日志系統(tǒng)拿到這些log荣挨,則在+startWithAppKey之前調用這個方法。
+testScriptInBundle
寫好的腳本上線前總要測試一下吧朴摊,就是用這個方法默垄。需要把main.js文件拖到項目中,并且不要調用+startWithAppKey:方法甚纲。
注意?诙А!=楦恕:測試完成一定要刪除main.js,血淋淋的教訓是鹃操,如果不刪除况既,線上的腳本down下來之后,無法確定會執(zhí)行哪個main.js,莫名其妙的問題组民,并且很難找到。切記切記
+setupCallback:
JSPatch 執(zhí)行過程中的事件回調悲靴,在以下事件發(fā)生時會調用傳入的 block:
typedef NS_ENUM(NSInteger, JPCallbackType){
JPCallbackTypeUnknow = 0,
JPCallbackTypeRunScript = 1, //執(zhí)行腳本
JPCallbackTypeUpdate = 2, //腳本有更新
JPCallbackTypeUpdateDone = 3, //已拉取新腳本
JPCallbackTypeCondition = 4, //條件下發(fā)
JPCallbackTypeGray = 5, //灰度下發(fā)
};
例如
[JSPatch setupCallback:^(JPCallbackType type, NSDictionary *data, NSError *error) {
switch (type) {
case JPCallbackTypeUpdate: {
NSLog(@"updated %@ %@", data, error);
break;
}
case JPCallbackTypeRunScript: {
NSLog(@"run script %@ %@", data, error);
break;
}
default:
break;
}
}];
+setupUserData:
定義用戶屬性臭胜,在+sync:
之前調用,用于條件下發(fā)癞尚,可以用來做AB測試耸三。什么是AB測試?自己去Google啊...
[JSPatch setupUserData:@{
@"userId": user.userId,
@"location": user.location,
@"gender":user.gender,
@"age":user.age
}];
發(fā)布補丁的時候選擇條件下發(fā)浇揩,寫入相應的條件就可以實現條件下發(fā)仪壮。例如圖中性別是女,年齡小于35歲的用戶顯示特定的內容胳徽。還可以選擇手機系統(tǒng)的版本积锅。
+setupDevelopment
開發(fā)者預覽模式,可以在 debug 模式下測試補丁效果养盗。
[JSPatch startAppWithKey:@""];
#ifdef DEBUG
[JSPatch setupDevelopment];
#endif
[JSPatch sync];
灰度下發(fā)
這個功能太實用了缚陷,選擇灰度下發(fā)可以按照比例灰度和人數灰度下發(fā)。比例灰度例如隨機挑選 30% 的設備生效往核;人數灰度比如只安裝1000臺設備箫爷。應用場景:
- 先下發(fā)一批看看效果,如果OK就全量下發(fā)
- 只對部分用戶下發(fā)聂儒,顯示特定的效果(和條件下發(fā)類似)
實戰(zhàn)
扯了這么多終于到實戰(zhàn)了虎锚。
背景:
- 項目已經集成了SDK
- 注冊過了平臺賬號
- 已經注冊了APP獲得了appkey
- 已經上線了集成過JSPatch SDK的app
- 這個上線的版本出現了大量的crash,crash率很高衩婚,不馬上解決窜护,老板就會馬上解決你...
線上的代碼是這樣的,數組訪問越界了
@implementation DSHomeViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *content = self.dataSource[[indexPath row]]; //可能會超出數組范圍導致crash
DSGoodsViewController *controller = [[DSGoodsViewController alloc] initWithContent:content];
[self.navigationController pushViewController:controller];
}
@end
修改源代碼
修改后的代碼
@implementation DSHomeViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(self.dataSource.length > indexPath.row){
NSString *content = self.dataSource[indexPath.row];
DSGoodsViewController *controller = [[DSGoodsViewController alloc] initWithContent:content];
[self.navigationController pushViewController:controller];
}
}
@end
編寫補丁腳本
打開JSPatch代碼轉換器,原生代碼轉化為JS代碼
轉化成功了谅猾,但是要數組柄慰,需要修改一下。常見問題 修改之后的腳本,為了能夠知道腳本運行税娜,第一行加上log
console.log('JSPatch Run Success');
require("DSGoodsViewController");
defineClass("DSHomeViewController", {
tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
var row = indexPath.row();
if (self.dataSource().length() > row) {
var content = self.dataSource()[row];
var controller = DSGoodsViewController.alloc().initWithContent(content);
self.navigationController().pushViewController(controller);
}
}
}, {});
測試腳本
寫好之后的腳本存為main.js放到項目中坐搔,在-application:didFinishLaunchingWithOptions:
方法中打開測試
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//[JSPatch startWithAppKey:myAPPKey];
//[JSPatch sync];
[JSPatch testScriptInBundle];
….
}
編譯運行會看到控制臺log輸出JSPatch Run Success
然后......怎么能少了調試呢
在Safari中斷點調試
開啟 Safari 調試菜單
Safari -> 偏好設置 -> 高級 -> 勾選[在菜單欄中顯示“開發(fā)”菜單]
啟動app進行調試
啟動APP -> Safari -> 開發(fā) -> 選擇你的機器 -> JSContext
在 iOS8 下,JSPatch 支持使用 Safari 自帶的調試工具對 JS 腳本進行斷點調試敬矩,界面大致長這樣
上傳腳本
- 在平臺上新建一個線上的版本
- 把調試通過的腳本main.js上傳到這個線上的版本
- 選擇全量下發(fā)(因為要搞定crash)
- 刪除本地的main.js
- 好了概行,等著下發(fā)之后crash率降下來,飯碗保住了
常見的問題
- 不能用
NSLog('xx')
弧岳,應該用console.log('xx')
- get property 記得加括號凳忙,例如
self.navigationItem()
业踏,而不是self.navigationItem
- 私有成員變量要用
self.valueForKey()
和self.setValue_forKey()
接口存取 - block 里不能直接使用 self,應該在block外定義var myself = self;
未完待續(xù)。涧卵。勤家。