限於iOS AppStore的審覈機制,一些新的功能的添加或者bug的修復,想作些節日專屬的活動等,幾乎都是不太可能的.從已有的經驗來看,也是有了一些比較經常使用的解決方案.本文先是會簡單說明對比大部分方案,而後會注重闡述基於JSPatch的在線更新機制的設計和實現.對於任何一家有必定用戶基礎的iOS應用來講,在線更新技術所產生的直接和間接價值都將遠遠超過100W.理解,並掌握它;實在沒有時間,就記住它,由於這篇文章不只僅是討論.html
實例地址:https://github.com/ios122/ios122html5
安裝本地全部補丁 --> 聯網更新補丁信息,並安裝有更新或新增長的補丁.注意此處的安裝,指的是執行如下JS文件中的代碼.此段代碼會替換某個類的默認實現.當App運行到須要某個類的某個被JSPatch替換的方法時,會走JS定義的邏輯,而再也不是源代碼中默認的邏輯.能夠看下DEMO.另外,咱們的應用和示例中都使用了Objection這個依賴注入的庫,你可能也要先溫習下: Objection,一個輕量級的Objective-C依賴注入框架react
mac上,獲取某個文件的md5值,直接在終端輸入命令:ios
md5 文件完整路徑.
關於校驗md5的代碼,其實最核心的是如何在oc中使用代碼獲取某個文件的md5值,而後進行比對.網上的示例不少,但可能不太靠譜,下面貼一段確實可行的,注意要引入系統庫 #include <CommonCrypto/CommonDigest.h>
:git
/** * 獲取文件的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 @""; } }
能夠絕不誇張的說,正確理解並定義補丁狀態,是整個在線更新機制最核心的一步,其餘的真的只是輔助:github
/** * 補丁狀態. */ 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.mweb
這個是必然要考慮的問題,一種方式是能夠在工程中放一個demo.js供Debug模式下調試;另外一種方式是本地返回固定的假數據,可是假數據自己的 JS文件地址,md5,版本號等都是真實的.編程
/** * 測試模式下,會執行此方法,以驗證某個JS文件的做用.默認使用本地demo.js. */ - (void)mcDebug { #ifdef DEBUG NSString * path = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"]; [self mcEvaluateScriptFile: path]; #endif }
另外,整個邏輯的實現,還使用了ReactCocoa來簡化邏輯代碼,若是不是很熟悉,能夠先看下:ReactiveCocoa,最受歡迎的iOS函數響應式編程庫(2.5版),沒有之一!react-native
這個要根據本身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,來支持在線更新,可是我作的時候,是不知道的,有點重複造輪子的感受.可是,也就兩天左右就實現了,只要能捋順補丁狀態控制的時機,代碼自己其實並無真正的技術難點.另外,官方的內測平臺,好像是閉源的,我不太敢用.
能夠先參考下文檔https://github.com/bang590/JSPatch/wiki/defineClass使用文檔,其實都是一一對應的語法轉義.若是代碼不少,官方還提供了轉換工具:https://github.com/bang590/JSPatchConvertor,可是結果僅供參考,可能還須要二次修改.
咱們的App,嵌入了JSPatch來進行Bug修復,已經經過審覈,而且恰好修復了一個很緊急的Bug.這裏不作過多的口水式的討論.
若是還在爲每次的APP更新而提心吊膽,請細細閱讀這篇文章;在線更新的,不只僅能夠用來修復Bug呦~