JSPatch

爲了解決因爲AppStore審覈而致使程序更新新版本慢,目前有兩種方案實時修復線上bug:
(1)直接使用UIWebView加載網絡上的HTML 的代碼,這樣若是有問題只須要更新服務器的HTML文件,用戶從新進入程序,加載新的HTML文件,整個程序就能更新,對用戶影響很是小。
(2)使用其餘腳本語言經過Runtime動態調用OC
<1>WaxPatch:它把Lua腳本語言和原生Objective-C應用編程(API)結合起來,經過Lua腳原本調用OC。 
<2>JSPatch:JS經過JavaScriptCore.framework調用Runtime來實現JS調用OC.
 
  1. 簡介
只須要在項目中引入極小的引擎文件,就可使用 JavaScript 調用任何 Objective-C 的原生接口,替換任意 Objective-C 原生方法。目前主要用於下發 JS 腳本替換原生 Objective-C 代碼,實時修復線上 bug。
JSPatch:JS是經過JavaScriptCore.frameworkdi調用Runtime,來實現 JS調用OC。
 

例如線上 APP 有一段代碼出現 bug 致使 crash:前端

@implementation JPTableViewController
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  NSString *content = self.dataSource[[indexPath row]];  //可能會超出數組範圍致使crash
  JPViewController *ctrl = [[JPViewController alloc] initWithContent:content];
  [self.navigationController pushViewController:ctrl];
}
...
@end

能夠經過下發這樣一段 JS 代碼,覆蓋掉原方法,修復這個 bug:git

//JS
defineClass("JPTableViewController", {
  //instance method definitions
  tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
    var row = indexPath.row()
    if (self.dataSource().length > row) {  //加上判斷越界的邏輯
      var content = self.dataArr()[row];
      var ctrl = JPViewController.alloc().initWithContent(content);
      self.navigationController().pushViewController(ctrl);
    }
  }
}, {})
除了修復 bug,JSPatch 也能夠用於動態運營,實時修改線上 APP 行爲,或動態添加功能。
 
JSPatch 須要使用者有一個後臺能夠下發和管理腳本,而且須要處理傳輸安全等部署工做,JSPatch 平臺幫你作了這些事,提供了腳本後臺託管,版本管理,保證傳輸安全等功能,讓你無需搭建一個後臺,無需關心部署操做,只需引入一個 SDK 便可當即使用 JSPatch。
 
  1. JSPatch優點
(1)JS比Lua在應用開發領域有更普遍的應用,目前前端開發和前端開發有融合趨勢,做爲擴展的腳本語言,JS是不二之選;
(2)JSPatch更符合Apple的規則,iOS Developer Program License Agreementi裏3.3.2提到不可動態下發可執行代碼,但經過蘋果JavaScriptCore.framework或WebKit執行的代碼除外,JS正是經過JavaScriptCore.framework執行的;
(3)使用系統內置的JavaScriptCore.framework,無需內嵌腳本引擎,體積小巧;
(4)支持block。
 
 
  1. JSPatch缺點
(1)相對於WaxPatch,JSPatch劣勢在於不支持iOS6,由於須要引入JavaScriptCore.framework;
目前內存的使用上會高於wax,持續改進中;
(2)存在風險:
JSPatch 讓腳本語言得到調用全部原生OC方法的能力,不像web前端把能力侷限在瀏覽器,使用上會有必定的安全風險;
<1>若在網絡傳輸過程當中下發明文,可能會被中間人篡改JS腳本,執行任意方法,盜取APP裏的相關信息,危機用戶信息和APP;
<2>若下載完後JS保存在本地沒有加密,在越獄的機器上用戶也能夠手動替換或篡改腳本
(3)風險控制:
<1>JSPatch腳本的執行權限很高,若在傳輸過程當中被中間人篡改,會帶來很大的安全問題,爲了防止這種狀況出現,在傳輸過程當中對JS文件進行了RSA簽名加密,流程以下:
服務端:計算JS文件MD5值,用RSA私鑰對MD5值進行加密,與JS文件一塊兒下發給客戶端;
客戶端:拿到加密數據,用RSA公鑰解密出MD5值。本地計算返回的JS文件MD5值。對比上述的兩個MD5值,若相等則校驗經過,取JS文件保存到本地
因爲RSA是非對稱加密,再沒有私鑰的狀況下第三方沒法加密對應的MD5值,也就沒法僞造JS文件,杜絕了JS文件在傳輸過程當中被篡改的可能。
<2>本地存儲
本地存儲的腳本被篡改的機會小不少,只在越獄機器上有點風險,對此JSPatch SDK在下載完腳本保存到時也進行了簡單的對稱加密,每次讀取時解密。
 
  1. JSPatch 平臺速度和穩定性如何?
經過 JSPatch 平臺上傳的腳本文件都會保存在 七牛雲存儲上,客戶端 APP 只跟七牛服務器通信,支持高併發,CDN分佈全國,速度和穩定性有保證。
 
 
  1. SDK接入
(1)得到AppKey
(2)集成SDK:
<1> 經過 cocoapods 集成:
在podfile中添加命令:
pod 'JSPatchPlatform'
再執行  pod install 便可。
<2>手動集成
下載 SDK 後解壓,將  JSPatchPlatform.framework 拖入項目中,勾選 "Copy items if needed",並確保 "Add to target" 勾選了相應的 target。
添加依賴框架:TARGETS -> Build Phases -> Link Binary With Libraries -> + 添加  libz.dylib 和  JavaScriptCore.framework。
(3)運行
在  AppDelegate.m 裏載入文件,並調用  +startWithAppKey: 方法,參數爲第一步得到的 AppKey。接着調用  +sync 方法檢查更新。例子:
 
#import <JSPatchPlatform/JSPatch.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     [JSPatch startWithAppKey:@"你的AppKey"];
     [JSPatch sync]; ...
}
@end
 
至此 JSPatch 接入完畢,下一步能夠開始在後臺爲這個 App  添加 JS 補丁文件了。
 
 
  1. 常見問題
若使用 XCode8 接入,須要在項目 Capabilities 打開 Keychain Sharing 開關,不然在模擬器下載腳本後會出現  decompress error, md5 didn't match 錯誤(真機不管是否打開都沒問題):
 
 
 
  1. SDK API
<1>  +startWithAppKey:
傳入在平臺申請的 appKey,啓動 JSPatch SDK。同時會自動執行已下載到本地的 patch 腳本。建議在  -application:didFinishLaunchingWithOptions: 開頭處調用。
 
<2>  +sync
與 JSPatch 平臺後臺同步,詢問是否有 patch 更新,若是有更新會自動下載並執行。
!!注意  +startWithAppKey: 並不會詢問後臺 patch 更新,必須調用  +sync 方法。
每調用一次  +sync 就會請求一次後臺,對於實時性要求不高的 APP,只需在  -application:didFinishLaunchingWithOptions: 處調用一次,這樣用戶會在啓動時去同步 patch 信息。對於實時性要求高的 APP,能夠在  -applicationDidBecomeActive: 處調用這個接口,這樣會在每次用戶喚醒 APP 時去同步一次後臺,請求次數會增多,但有 patch 更新時用戶會及時收到。
 
<3>  +setupLogger:
JSPatch SDK 裏會打一些請求和執行的log,默認是以  NSLog() 打出,若你的 APP 有本身的日誌系統,但願把 log 打在你的日誌系統裏,能夠在調用 +startWithAppKey 以前調用這個接口:
[JSPatch setLogger:^(NSString *msg) {
    //msg 是 JSPatch log 字符串,用你自定義的logger打出
    YOUR_APP_LOG(@"%@", msg);
}];
 
<4>  +testScriptInBundle
用於發佈前測試腳本,調用後,會在當前項目的 bundle 裏尋找 main.js 文件執行。注意不能同時調用  +startWithAppKey: 方法,測試完成後須要刪除。
 
<5>  +setupCallback:
JSPatch 執行過程當中的事件回調,在如下事件發生時會調用傳入的 block:
typedef NS_ENUM(NSInteger, JPCallbackType){
    JPCallbackTypeUnknow        = 0,
    JPCallbackTypeRunScript     = 1,    //執行腳本
    JPCallbackTypeUpdate        = 2,    //腳本有更新
    JPCallbackTypeUpdateDone    = 3,    //已拉取新腳本
    JPCallbackTypeCondition     = 4,    //條件下發
    JPCallbackTypeGray          = 5,    //灰度下發
};

舉例:github

[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;
    }
}];
 
<6>  +setupUserData:
定義用戶屬性,在  +sync: 以前調用,用於條件下發,例如: 
[JSPatch setupUserData:@{
    @"userId": @"100867", 
    @"location": @"guangdong"
}];
 
<7>  +setupRSAPublicKey:
自定義 RSA key,在  +sync: 以前調用,詳見  自定義 RSA 密鑰
 
<8> +setupDevelopment
開發預覽模式,下發補丁時若選擇開發預覽模式下發,只會對調用過  +setupDevelopment的客戶端生效。
建議在  DEBUG 時設置,詳見  開發預覽
 
 
  1. 使用範例
JSPatch 能夠替換線上原生代碼,達到實時修復 bug 的目的,接下來舉個例子說明若要通過 JSPatch 平臺下發 JS 腳本修復 bug,須要進行哪些步驟。

首先項目必須接入 JSPatch SDK,並關聯 AppKey,線上版本必須帶有這個 SDK。web

假設已接入 JSPatch SDK 的某線上 APP 發現一處代碼有 bug 致使 crash:編程

@implementation XRTableViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      NSString *content = self.dataSource[[indexPath row]];
      //可能會超出數組範圍致使crash
     XRViewController *controller = [[JPViewController alloc] initWithContent:content];
     [self.navigationController pushViewController:controller];
}
@end

上述代碼中取數組元素處可能會超出數組範圍致使 crash,對此咱們寫了以下 JS 腳本準備替換上述方法修復這個 bug:數組

//main.js
defineClass("XRTableViewController", {          
     tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
      var row = indexPath.row()
     if (self.dataSource().length > row) {//加上判斷越界的邏輯
     var content = self.dataArr()[row];
     var controller = XRViewController.alloc().initWithContent(content);
   self.navigationController().pushViewController(controller);                    }
}
})

注意在 JSPatch 平臺的規範裏,JS腳本的文件名必須是 main.js。接下來就看如何把這個 JS 腳本下發給全部用戶。瀏覽器

測試

在上線以前須要對腳本進行本地測試,看看運行是否正常。SDK 提供了方法 +testScriptInBundle 用於發佈前的測試:七牛雲存儲

#import <JSPatch/JSPatch.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {                                    
[JSPatch testScriptInBundle];
….
}
@end

調用這個方法後,JSPatch 會在當前項目的 bundle 裏尋找 main.js 文件執行,效果與最終線上用戶下載腳本執行同樣,測試完後就能夠準備上線這個腳本。緩存

注意 +testScriptInBundle 不能與 +startWithAppKey: 一塊兒調用,+testScriptInBundle 只用於本地測試,測試完畢後須要去除。安全

添加版本

進入 JSPatch 平臺後臺,在個人 APP 裏選擇這個 APP,點擊添加版本。填入當前線上 APP 的版本號,能夠在項目 TARGETS -> General -> version 上能夠找到:

注意這裏版本號必須一致,JSPatch 平臺會只針對這個版本號下發對應的 JS 腳本,若版本號對應不上,客戶端也就請求不到相應的 JS 腳本。

添加JS腳本

點擊進入剛添加的版本,上傳 main.js 便可。

上傳能夠直接全量下發,也能夠選擇 開發預覽 或 灰度或條件下發,也可使用自定義 RSA key 對腳本進行加密簽名。

上傳完成後,對應版本的 APP 會請求下載這個腳本保存在本地,之後每次啓動都會執行這個腳本。至此線上 bug 修復完成。

修改/刪除JS腳本

若後續須要對這個腳本進行修改,能夠從新上傳新的腳本,APP 客戶端會在請求時發現腳本已更新,下載最新腳本覆蓋原來的,下次啓動時執行。

 
若想直接取消某個 APP 版本的 JS 腳本補丁,能夠直接在 APP 版本界面刪除此 APP 版本,APP 客戶端會在請求時發現腳本已被刪除,即刻刪除本地 JS 腳本文件,下次啓動時再也不加載。
 
 
 
  1. 接入擴展
JSPatch 經過擴展實現 C 函數調用 / GCD / 鎖 等功能,能夠在  JSPatch github 項目 上看到。JSPatch 平臺 SDK 默認沒有接入這些擴展,若要使用這些功能,須要另外接入。
 
經過 cocoapods 接入
JSPatch 平臺在 cocoapods 有三個 subspec,分別是: <1>JSPatchPlatform/Extensions
包含了  擴展 里根目錄的文件,包括:
JPDispatch: 提供完整GCD接口  
JPLocker: 提供@synchronized接口  
JPNumber: 包裝 NSNumber  
JPProtocol: 提供@protocol接口  
JPSpecialInit: 特殊類 UIWebview 和 NSCalendar 的初始化
<2>JSPatchPlatform/JPCFunction
提供調用任意 C 函數的接口,詳見  C 函數調用
<3>JSPatchPlatform/JPCFunctionBinder
提供了一些經常使用 CoreFoundation C 函數接口的轉接。
能夠在 podfile 裏直接接入:
pod 'JSPatchPlatform'
pod 'JSPatchPlatform/Extensions'
pod 'JSPatchPlatform/JPCFunction'
而後執行  pod install 即完成接入。
 
手動接入
若沒有使用 cocoapods,能夠在  JSPatch github 項目 下載相應的擴展文件,拖入項目。
 
 
  1. 安全問題
傳輸安全
JSPatch腳本的執行權限很高,若在傳輸過程當中被中間人篡改,會帶來很大的安全問題,爲了防止這種狀況出現,咱們在傳輸過程當中對JS文件進行了RSA簽名加密,流程以下:

服務端:

  1. 計算 JS 文件 MD5 值。
  2. 用 RSA 私鑰對 MD5 值進行加密,與JS文件一塊兒下發給客戶端。

客戶端:

  1. 拿到加密數據,用 RSA 公鑰解密出 MD5 值。
  2. 本地計算返回的 JS 文件 MD5 值。
  3. 對比上述的兩個 MD5 值,若相等則校驗經過,取 JS 文件保存到本地。
 
因爲 RSA 是非對稱加密,在沒有私鑰的狀況下第三方沒法加密對應的 MD5 值,也就沒法僞造 JS 文件,杜絕了 JS 文件在傳輸過程被篡改的可能。
本地存儲
本地存儲的腳本被篡改的機會小不少,只在越獄機器上有點風險,對此 JSPatch SDK 在下載完腳本保存到本地時也進行了簡單的對稱加密,每次讀取時解密。
 
 
 
  1. 自定義RSA祕鑰
對 RSA 密鑰的做用詳見 安全問題
客戶端和 JSPatch 後臺默認有一對 RSA 密鑰,默認會用這對密鑰進行加解密驗證。
若對安全要求較高,能夠按如下步驟自定義 RSA 密鑰:
<1>生成 RSA 密鑰
在 Mac 終端上執行  openssl,再執行如下三句命令,生成 PKCS8 格式的 RSA 公私鑰,執行過程當中提示輸入密碼,密碼爲空(直接回車)就行。
openssl >
genrsa -out rsa_private_key.pem 1024
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM –nocrypt
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
 
這樣在執行的目錄下就有了  rsa_private_key.pem 和  rsa_public_key.pem 這兩個文件。這裏生成了長度爲 1024 的私鑰,長度可選 1024 / 2048 / 3072 / 4096 ...。
<2>SDK 設置 RSA Public Key
客戶端接入 SDK 後調用  +setupRSAPublicKey: 設置自定義的 RSA Public Key,注意應該在  +sync 以前調用,由於  +sync 可能會下載到腳本,這時已經要用 RSA key 去驗證了。
Public Key 以字符串的方式傳入,注意換行處要手動加換行符 \n,例:
//rsa_public_key.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApgeqKYKPVFk1dk2JGrKv
EaSqqXxU2S1x32xn2M2jWK/lz7YOPRFcPhH8UgBgpUQGqbW2ooOrtlE0Ur6WHOgZ
HvozA71xKEgpQhLbX8ourcyC638zfEQJ3aUezjy5ADzlIAWr3ayBYmLBYj4OkRRG
bffxwA+i16jNVFWJFzgCrRs44cpn+nX0VsNrNjntt59J3xIhMGE+eQ2K9WDwYmv4
sw8+3MsW++z2Uornmi9v2atZnBKd/dBsGz05d++NBks7b2ot/TAiMRnit+VNTZrs
1rYQOcoCJlMUK4GDkK6bdKAPfVcD5vy2PAxDA84P2txcSkFozmZABcVvSyASB6Bn
MQIDAQAB
-----END PUBLIC KEY-----
[JSPatch setupRSAPublicKey:@"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApgeqKYKPVFk1dk2JGrKv\nEaSqqXxU2S1x32xn2M2jWK/lz7YOPRFcPhH8UgBgpUQGqbW2ooOrtlE0Ur6WHOgZ\nHvozA71xKEgpQhLbX8ourcyC638zfEQJ3aUezjy5ADzlIAWr3ayBYmLBYj4OkRRG\nbffxwA+i16jNVFWJFzgCrRs44cpn+nX0VsNrNjntt59J3xIhMGE+eQ2K9WDwYmv4\nsw8+3MsW++z2Uornmi9v2atZnBKd/dBsGz05d++NBks7b2ot/TAiMRnit+VNTZrs\n1rYQOcoCJlMUK4GDkK6bdKAPfVcD5vy2PAxDA84P2txcSkFozmZABcVvSyASB6Bn\nMQIDAQAB\n-----END PUBLIC KEY-----"];
<3>使用 Private Key 下發腳本

下發腳本時在發佈腳本界面勾選 使用自定義RSA Key 選項,會出現文件上傳框,選擇本地的 rsa_private_key.pem 文件,與腳本一同上傳,JSPatch 平臺會使用這個上傳的 Private Key 對腳本 MD5 值進行加密,再下發給客戶端。若客戶端通過上述第二步設置了對應的 Public Key,就會用設置的 Public Key 對腳本進行驗證,驗證經過後運行腳本,不然不會運行。

 
這裏上傳的  rsa_private_key.pem 只是一次性使用,不會保存在服務端,因此只有經過用戶本身保存的  rsa_private_key.pem 文件才能夠針對 APP 下發腳本,即便 JSPatch 平臺或者七牛雲被黑,第三方也沒法對你的 APP 下發惡意腳本(能夠下發,但驗證不過,不會執行),保證安全性。 rsa_private_key.pem 請妥善保管,避免泄露。
 
 
  1. 開發預覽
從 SDK 1.4 開始支持在發佈腳本時先針對開發版本下發,測試無問題再進行全量下發或灰度/條件下發。
首先在代碼中開啓開發模式,在  +sync 以前調用  setupDevelopment 方法,建議只在 debug 模式下開啓:
[JSPatch startAppWithKey:@""];
#ifdef DEBUG
[JSPatch setupDevelopment];
#endif
[JSPatch sync];
 
接着在發佈補丁時選擇  開發預覽,就能夠在 debug 模式下測試這個補丁。測試完成後能夠選擇全量下發或灰度/條件下發,下發給現網用戶。
注:若已發佈的 APP 接入了舊的 SDK,也能夠經過這種方式進行測試。發佈腳本時選擇開發預覽,則這個新版本只會在接入 SDK 1.4 並開啓開發模式時生效,對接入舊 SDK 無影響,測試完成後全量下發纔會生效。
 
 
  1. 灰度與條件下發
從  SDK 1.2 版本開始支持腳本的灰度與條件下發。
灰度
在後臺發佈補丁腳本時能夠選擇灰度下發,並選擇灰度用戶百分比,這個補丁就會按選定的百分比只對這個比例的用戶起做用。例如選擇灰度 30%,那這個補丁腳本只會在全部接入的設備中隨機挑選 30% 的設備生效。
灰度下發無需 SDK 額外設置,只需接入的 SDK 版本在 1.2 以上。
灰度發佈後後續能夠修改這個灰度值,便於逐漸增長灰度數量,直到全量發佈。
 
條件下發
在後臺發佈補丁腳本時能夠選擇條件下發,而後填入條件語句,只有知足條件的設備纔會執行這個補丁腳本,條件語句由 key/value/運算符組成,示例: userId==10000876iOS>9.0&&isMale==1

條件語句裏用到的 key/value 須要事先在 APP 裏經過 +setupUserData: 設置,支持設置多個字段,用 NSDictionary 表示,例如能夠設置當前登陸的用戶ID以及性別:

//_userId = @"1000876"
//_isMale = @(1)
[JSPatch setupUserData:@{@"userId": _userId, @"isMale": _isMale}];

這樣在下發腳本時填入條件 userId==1000876 後,這個腳本就只對這個用戶生效,若是填入 isMale==0 則對這個用戶不生效,對其餘在 SDK 設置了 @"isMale": @"0" 的用戶生效。

條件語句規則

  1. 支持符號 && || == != >= <= > <,意思跟程序裏同樣。
  2. 用比較符號時 >= <= > < 會把值轉爲數值進行對比。例如 userId>200000,即便客戶端調用 +setupUserData: 接口時設置的 userId 字段是字符串,也會轉爲數值進行對比。
  3. 使用 == != 符號時,會以字符串形式判斷是否相等,例如 1.0 == 1 結果是 NO。
  4. 等式的值不須要引號,字符串也不須要,例如:location!=guangdong
  5. 支持多個條件,例如:userId!=31242&&location==guangdong&&name==bang
  6. 若多個條件裏同時有 && 和 ||&& 的優先級較高。例如 userId<200000||location==guangdong&&name==bang,會先分別計算 userId<200000 和 location==guangdong&&name==bang 的結果,再進行 || 運算。
 
條件更新規則
在發佈腳本時用條件下發後,發佈後能夠不斷修改條件,但在以前已經命中了條件執行了腳本的設備,不會由於修改條件後變爲不命中,也就是說已經命中過條件執行了腳本的設備,不會再被條件的更新影響。舉個例子:

在發佈腳本1時設條件爲 userId==1000876,某設備A設置了 @{@"userId": @"1000876"}命中了這個條件,執行了這個腳本1。設備B設置了 @{@"userId": @"2000876"} 沒有命中。

接着在後臺修改條件爲 userId>=2000000 ,設備A並不符合這個條件,但由於以前的條件命中過,因此設備A不會再受這個改變影響,繼續執行腳本1。設備B命中了這個條件,也執行了腳本1。

此外若想撤銷條件全量發佈,提交空條件便可。

 
內置信息
除了用戶手動設置的 userData,SDK 裏還內置了兩個信息可供條件判斷: iOS 和  isPad,分別表示 iOS 版本號和是否iPad,不須要設置就能夠拿這兩個字段用於條件判斷。

例如只針對 iOS8 的 iPad 下發,能夠直接寫這個條件:iOS>=8.0&&iOS<9.0&&iPad==1

注意 iOS 版本號只會精確到兩位,例如 9.2.1 會記錄成 9.2,iOS==9.2 會命中 9.2.x 版本。

注意事項

  1. +setupUserData: 接口要在 +sync: 接口以前調用。
  2. 對於 SDK 1.1 及如下版本會無視任何條件和灰度值,直接全量接收。
 
 
 
  1. 在線參數
JSPatch 平臺附帶了在線參數功能,能夠直接向 APP 下發多個固定的參數,對APP進行動態配置。
使用
在每一個 APP 側邊欄的在線參數入口進入,新增你想要下發的參數名和參數值,例如  參數名:name  參數值:bang
APP 端在  +application:didFinishLaunchingWithOptions: 裏調用  +updateConfigWithAppKey: 方法,傳入  appKey,APP 就會在調用處發請求獲取剛纔設置的在線參數。
獲取成功後,能夠經過  +getConfigParams 拿到全部參數,也能夠經過  +getConfigParam:接口拿到單個參數,例如:
NSDictionay *configs = [JSPatch getConfigParams];
//configs == @{@"name": @"bang"}

NSString *name = [JSPatch getConfigParam:@"name"];
//name == bang
 
設置
若是想在  +updateConfigWithAppKey: 的請求返回時進行一些操做,能夠經過  + setupUpdatedConfigCallback: 接口設置 callback:
[JSPatch setupUpdatedConfigCallback:^(NSDictionary *configs, NSError *error) {
    NSLog(@"%@ %@", configs, error);
}];

爲了不重複請求浪費資源,默認 +updateConfigWithAppKey: 接口請求時間間隔至少爲30分鐘,也就是30分鐘內屢次調用 +updateConfigWithAppKey: 只會請求一次。若想 APP 對在線參數響應更實時,能夠經過 +setupConfigInterval: 接口修改這個間隔值。

注意
  1. 在線參數功能與 JSPatch 腳本下發功能獨立,互不影響。
  2. 在線參數的計費方式一樣按請求次數計算,每調用一次 +updateConfigWithAppKey: 方法算一次請求。
 
 
 
  1. 實時監控(new)
標準版用戶能夠看到補丁下發的實時監控信息。(須要接入 SDK 1.6 以上版本)

 
實時監控能夠看到當前補丁下發成功的數量,以圖表方式展現過去一天裏每一個小時的累計成功數,或過去七天裏天天的成功數,幫你實時掌握補丁應用狀況。
當有新的補丁下發時,若客戶端拉取補丁失敗,會自動在下次調用  [JSPatch sync] 時從新拉取,重試 3 次失敗後,會上報失敗數據,在實時監控這裏也能夠看到每一條失敗數據以及對應的錯誤碼,方便排查問題。
接入 SDK 1.6 或以上版本後監控信息會自動發送,無需配置。監控信息只包括 補丁應用成功 以及 補丁應用失敗錯誤碼 這兩種信息,不會上報其餘信息。
 
 
  1. CLI API
JSPatch 平臺提供了API接口,能夠直接用命令行上傳和修改補丁,方便開發者集成到本身的自動化發佈腳本上。
上傳補丁
API
POST http://jspatch.com/Apps/uploadPatch
@params email 登陸郵箱
@params password 登陸密碼
@params appKey APP惟一鍵值
@params appVersion APP版本號
@params gray (可選)灰度策略,值爲1-9,表明10%-90%
@params condition (可選)條件下發
@params patch[] 補丁文件
@params rsaKey (可選)rsa private密鑰文件

//失敗返回
@return {errMsg: ''}

//成功返回
@return {succ: 1, patchVersion: {$patchVersion}}
示例
在命令行經過 curl 上傳補丁:
curl -F 'email=test@qq.com' -F 'password=test1234' -F 'appKey=2ba21d234fa69915' -F 'appVersion=2.0' -F 'gray=4' -F 'patch[]=@main.js' http://jspatch.com/Apps/uploadPatch
修改補丁
上傳補丁時,若使用了灰度策略或條件下發,可使用這個API修改灰度值和條件值。
API
POST http://jspatch.com/Apps/updatePatch
@params email 登陸郵箱
@params password 登陸密碼
@params appKey APP惟一鍵值
@params appVersion APP版本號
@params gray (可選)修改灰度策略,值爲1-9,表明10%-90%
@params condition (可選)修改條件下發規則
@params all (可選)修改成全量下發

//失敗返回
@return {errMsg: ''}

//成功返回
@return {
  succ: 1, 
  patch: {
    patchID: 5804,
    gray: 3,
    condition:null,
    isDev:0
  }
}
示例
在命令行經過 curl 修改補丁:
curl -F 'email=test@qq.com' -F 'password=test1234' -F 'appKey=2ba21d234fa69915' -F 'appVersion=2.0' -F 'condition=userId=21' http://jspatch.com/Apps/updatePatch
 
 
 
  1. 常見問題
爲何腳本沒有生效?
首先請確保按 使用範例的步驟作了。若出現問題,能夠參考如下調試思路:
(一) 肯定 JS 腳本書寫正確
首先確保沒有 JS 語法錯誤,能夠用一些 在線工具檢測寫的 JS 腳本有沒有問題。
請按照  github wiki 裏的規則寫 JSPatch 腳本,幾個常犯的錯誤: 
  1. 不能用 NSLog('xx'),應該用 console.log('xx')
  2. get property 記得加括號,例如 self.navigationItem(),而不是 self.navigationItem
  3. 私有成員變量要用 self.valueForKey() 和 self.setValue_forKey() 接口存取。
  4. block 裏不能直接使用 self
其餘更多請參見 wiki 的  基礎用法 和  常見問題
先在本地用  +testScriptInBundle 接口執行腳本看有沒有問題,(詳情參照 使用範例),若沒達到預期效果,能夠一步步調試,第一步請在  main.js 開頭打  console.log('run success'),肯定 XCode 控制檯有輸出這條 log,肯定腳本有被執行到,再進行其餘調試。通常調試使用  console.log() 就足夠,如有更多需求能夠用  Safari斷點調試
 
(二) 檢查腳本上傳下載
先確保  appKey 和  版本號 沒有錯誤。
如有問題,看控制檯輸出的 log,JSPatch SDK 默認會打一些請求和執行的 log:
2016-04-27 19:04:42.212 ... JSPatch: runScript
2016-04-27 19:04:42.399 ... JSPatch: evaluated script, length: 28
打出這兩句 log 表示執行了腳本,length 表示腳本的大小。
--
2016-04-27 19:04:42.399 ... JSPatch: request http://7xkfnf.com1.z0.glb.clouddn.com/6d2fddf24c5d8af2/1.0?v=1461755082.399732
2016-04-27 19:04:42.621 ... JSPatch: request success {
    v = 2;
}

這兩句表示請求到了當前版本補丁版本號,這裏 url 裏的 6d2fddf24c5d8af2 是 appkey,後續跟的 1.0 是 App 版本號,能夠檢查下這兩個值是否正確。若 url 不正確或者腳本沒有正確上傳,這裏會返回 error = "Document not found"

--

2016-04-27 19:09:43.798 ... JSPatch: updateToVersion: 2
2016-04-27 19:09:43.798 ... JSPatch: request file http://7xkfnf.com1.z0.glb.clouddn.com/6d2fddf24c5d8af2/1.0/file2
2016-04-27 19:09:43.900 ... JSPatch: request file success, data length:3072
2016-04-27 19:09:43.908 ... JSPatch: updateToVersion: 2 success

這幾句表示檢測到的補丁版本號比本地版本更新,去下載補丁文件,下載後會當即執行,到這一步應該就沒問題了。若這個版本的補丁以前已經下載過,就不會再下載。

 
另外在 SDK 1.3.1 及以前的版本,若按這個流程下發補丁:  新建 APP 版本 -> 上傳補丁 -> 刪除APP版本 -> 新建同一個APP版本 -> 上傳補丁,會中緩存邏輯,致使請求到的腳本是刪除 APP 以前上傳的舊補丁。這時只須要再上傳一次補丁更新版本號就能夠了。
有問題照上述點一個個檢查,就能夠解決了,這裏會儘可能列出可能會出現的問題,平臺也會持續改進易用性。若還有問題,請查看  平臺文檔 以及  JSPatch 文檔
 
出現 decompress error
若使用 XCode8 接入,須要在項目 Capabilities 打開 Keychain Sharing 開關,不然在模擬器下載腳本後會出現  decompress error, md5 didn't match 錯誤(真機不管是否打開都沒問題):
相關文章
相關標籤/搜索