價值100W的經驗分享: 基於JSPatch的iOS應用線上Bug的即時修復方案,附源碼.

限於iOS AppStore的審覈機制,一些新的功能的添加或者bug的修復,想作些節日專屬的活動等,幾乎都是不太可能的.從已有的經驗來看,也是有了一些比較經常使用的解決方案.本文先是會簡單說明對比大部分方案,而後會注重闡述基於JSPatch的在線更新機制的設計和實現.對於任何一家有必定用戶基礎的iOS應用來講,在線更新技術所產生的直接和間接價值都將遠遠超過100W.理解,並掌握它;實在沒有時間,就記住它,由於這篇文章不只僅是討論.react

實例地址:https://github.com/ios122/ios122ios

幾種在線更新方案的對比: 爲何是JSPatch?

方案一: 申請"加急審覈"

  • 方法: 提交應用時,選擇"加急審覈".
  • 優勢: 操做簡單,只須要從新上傳應用便可;
  • 缺點:"加急審覈",確定是不能常用的.
  • 簡評: 我想,這多是大多數公司遇到緊急問題時,最常使用的方案.一個應用,每一年是有若干次機會申請"加急審覈",來縮短應用新版本的審覈週期.一般審覈週期是7天左右;"加急審覈",一般只須要3天左右.

方案二: 使用 webview + Html5 頁面

  • 方法: 特定的可能須要常常換的頁面使用WebView來顯示,內部使用Html5的內容來填充.當須要改變頁面時,只須要改變下服務器接口返回的內容便可.
  • 優勢: 對於內容的更新,足夠靈活和迅速.
  • 缺點: 沒法修復非HTML5頁面的Bug;Html5 交互和UI一般遜色於原生頁面.
  • 簡評: 混合應用經常使用的方式,如PhoneGap等;對於大多數原生應用來講,此方案基本無適用性.

方案三: 編寫基於ReactNative的應用

  • 方法: 使用 ReactNative 來編寫應用或應用的部分頁面,更多介紹參見: React Native 官方文檔中文版
  • 優勢: 原生UI,原生交互,支持服務器方式在線更新應用.
  • 缺點: 對於非ReactNative編寫的頁面無能爲力.
  • 簡評: 我的主觀是很看好 ReactNative的,也在慢慢踩坑;但現實是大部分公司的已有項目是基於Objetive-C的,因此基於ReactNative的在線更新策略,目前對於大多說公司來講也並不具備可行性.

方案四: 基於JSPatch實如今線補丁式更新

  • 方法: 在本身的項目中引入JSPatch庫,而後參見下文繼續討論的方案細節實施便可.JSPatch的入門使用,參見: http://www.ios122.com/2015/11/jspatch/
  • 優勢: 支持操做全部工程中引入的CocoaTouch庫與各類第三方庫.可徹底自由定義與重寫已有代碼的邏輯.
  • 缺點: JS語法操做API,語法轉換有必定成本.
  • 簡評: 大多數時候,咱們須要的只是重寫下某個方法,甚至某個判斷,某個默認值,就能夠很好地修復某個線上的Bug.因此,JSPatch,已經夠用了.固然,若是是對於複雜的新功能的添加的話,建議仍是提交審覈吧.另外,不得不說一句,JSPatch + ReactNatvie 未來或許會成爲一個很強力的組合,前者側重於Bug的修復,後者側重於複雜新需求的添加.本文接下來的篇幅將注重討論基於JSPatch的線上Bug的即時修復方案.

關於使用JSPatch幾個技術點的分析與實現.

基本實現原理

安裝本地全部補丁 --> 聯網更新補丁信息,並安裝有更新或新增長的補丁.注意此處的安裝,指的是執行如下JS文件中的代碼.此段代碼會替換某個類的默認實現.當App運行到須要某個類的某個被JSPatch替換的方法時,會走JS定義的邏輯,而再也不是源代碼中默認的邏輯.能夠看下DEMO.另外,咱們的應用和示例中都使用了Objection這個依賴注入的庫,你可能也要先溫習下: [Objection,一個輕量級的Objective-C依賴注入框架git

](http://www.ios122.com/2015/11/objection/)github

文件 md5 值的獲取與校驗

mac上,獲取某個文件的md5值,直接在終端輸入命令:web

md5 文件完整路徑.

關於校驗md5的代碼,其實最核心的是如何在oc中使用代碼獲取某個文件的md5值,而後進行比對.網上的示例不少,但可能不太靠譜,下面貼一段確實可行的,注意要引入系統庫 #include <CommonCrypto/CommonDigest.h>:編程

/**
 *  獲取文件的md5信息.
 *
 *  @param path 文件路徑.
 *
 *  @return 文件的md5值.
 */
-(NSString *)mcMd5HashOfPath:(NSString *)path
{
    NSFileManager *fileManager = [NSFileManager defaultManager];
    
    // 確保文件存在.
    if( [fileManager fileExistsAtPath:path isDirectory:nil] )
    {
        NSData *data = [NSData dataWithContentsOfFile:path];
        unsigned char digest[CC_MD5_DIGEST_LENGTH];
        CC_MD5( data.bytes, (CC_LONG)data.length, digest );
        
        NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
        
        for( int i = 0; i < CC_MD5_DIGEST_LENGTH; i++ )
        {
            [output appendFormat:@"%02x", digest[i]];
        }
        
        return output;
    }
    else
    {
        return @"";
    }
}

補丁狀態的管理

能夠絕不誇張的說,正確理解並定義補丁狀態,是整個在線更新機制最核心的一步,其餘的真的只是輔助:react-native

/**
 *  補丁狀態.
 */
typedef enum : NSUInteger {
    YFPatchModelStatusUnKnownError, //!< 未知錯誤.
    YFPatchModelStatusUnInstall, //!< 還沒有開始安裝.應用初始時,全部本地補丁狀態均爲此;補丁更新或新增的補丁;在下載完成後,狀態也會設置爲此.
    YFPatchModelStatusSuccess, //!< 安裝成功.
    YFPatchModelStatusFileNotExit, //!< 本地補丁文件不存在.
    YFPatchModelStatusFileNotMatch, //!< 本地補丁MD5與給定的MD5值不匹配.
    YFPatchModelStatusUpdate, //!< 此補丁有更新.即服務器最新返回的補丁列表中包含此補丁,但補丁的md5或url已改變.
    YFPatchModelStatusAdd //!< 此補丁爲新增的.即服務器最新返回的補丁列表中新添加的補丁.
} YFPatchModelStatus;

補丁狀態的具體管理策略,參見 https://github.com/ios122/ios122/blob/master/iOS122/iOS122/samples/JSPatchOnline/patch/YFPatchViewModel.m緩存

如何在本地測試JS可用性

這個是必然要考慮的問題,一種方式是能夠在工程中放一個demo.js供Debug模式下調試;另外一種方式是本地返回固定的假數據,可是假數據自己的 JS文件地址,md5,版本號等都是真實的.安全

/**
 *  測試模式下,會執行此方法,以驗證某個JS文件的做用.默認使用本地demo.js.
 */
- (void)mcDebug
{
#ifdef DEBUG
    NSString * path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    [self mcEvaluateScriptFile: path];
#endif

}

補丁的增刪改查.

  • 增:服務器返回的補丁,本地不存在時,會默認下載存儲,並執行.
  • 刪: 服務器返回的補丁集中,不包含本地的某個補丁,則此補丁下次不會再被執行.
  • 改: 服務器返回的補丁,本地包含,但md5值變化,此時會從新下載此補丁.
  • 查: 會默認在應用啓動時,執行全部存在,且md5值匹配的補丁.補丁集的信息,會在每次聯網更新時更新.此處使用的是一個緩存庫https://github.com/pinterest/PINCache

另外,整個邏輯的實現,還使用了ReactCocoa來簡化邏輯代碼,若是不是很熟悉,能夠先看下:ReactiveCocoa,最受歡迎的iOS函數響應式編程庫(2.5版),沒有之一!ruby

關於安全性

這個要根據本身App的狀況,實際考慮下.咱們的App網絡接口是基於HTTPS的,因此不存在中間人攻擊的狀況.因此能夠保證md5和文件路徑是咱們本身可控的,因此只作了最基本的md5校驗.具體你們能夠參考下官方的基於JSPatch的在線更新補丁實踐http://jspatch.com/Docs/security:

JSPatch腳本的執行權限很高,若在傳輸過程當中被中間人篡改,會帶來很大的安全問題,爲了防止這種狀況出現,咱們在傳輸過程當中對JS文件進行了RSA簽名加密,流程以下:

服務端:

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

拿到加密數據,用 RSA 公鑰解密出 MD5 值。
本地計算返回的 JS 文件 MD5 值。
對比上述的兩個 MD5 值,若相等則校驗經過,取 JS 文件保存到本地。

官方有個內測的平臺http://jspatch.com,來支持在線更新,可是我作的時候,是不知道的,有點重複造輪子的感受.可是,也就兩天左右就實現了,只要能捋順補丁狀態控制的時機,代碼自己其實並無真正的技術難點.另外,官方的內測平臺,好像是閉源的,我不太敢用.

關於JS文件的編寫.

能夠先參考下文檔https://github.com/bang590/JSPatch/wiki/defineClass使用文檔,其實都是一一對應的語法轉義.若是代碼不少,官方還提供了轉換工具:https://github.com/bang590/JSPatchConvertor,可是結果僅供參考,可能還須要二次修改.

關於 APPstore 審覈

咱們的App,嵌入了JSPatch來進行Bug修復,已經經過審覈,而且恰好修復了一個很緊急的Bug.這裏不作過多的口水式的討論.

小結

若是還在爲每次的APP更新而提心吊膽,請細細閱讀這篇文章;在線更新的,不只僅能夠用來修復Bug呦~

相關文章
相關標籤/搜索