【进阶篇】iOS解决方案JSPatch

APP上线之后发现BUG了怎么办,等待下一次更新可能是几天之后,对于用户来说这或许就是一场灾难,有没有更好的解决办法?

更新完毕

iOS App审核是所有iOS开发者心中的一座山,漫长的等待时间如果在正常的版本更新时还好,但是在线上出现严重BUG时,没一分每一秒对我们来说都是一种煎熬,面对这样的问题,难道腾讯,阿里都跟我们一样吗?

当然不是
看看他们的解决方案:
######1、微信为代表的JSPatch:https://github.com/bang590/JSPatch ######2、阿里为代表的Wax:https://github.com/alibaba/wax
好几次App刚刚审核通过,却发现其中有致命的BUG,修改的话可能就是一两行代码就能搞定,但是苹果的审核时间让你起码几天面对这个BUG提心吊胆,对于饱受这份折磨的我来说,JSPatch简直就是救世主,话不多说,看都有谁在用这个:图片源自http://using.jspatch.org/ 【JSPatch使用统计社区】
![image](/content/images/2016/01/QQ20160113-0-2x.png)

这些大厂都在用着一套可以实现hit code push的解决方案
让我们来认识一下JSPatch吧,原作者博客地址:

http://blog.cnbang.net/


JSPatch原理

JSPatch的基本原理就是通过JS代码,利用OC运行时的特性,已达到修改程序代码,让App具备hit code push的能力,更详细的原理可以去看上面原作者的博客,里面非常详细的讲解了JSPatch的底层实现。 JS文件通过从服务器下载到本地,所有使用时需要服务器的一定配合。 ----

JSPatch的两个安全问题

1、传输安全:JS 脚本可以调用任意 OC 方法,权限非常大,若被中间人攻击替换代码,会造成较大的危害。 2、执行安全:下发的 JS 脚本灵活度大,相当于一次小型更新,若未进行充分测试,可能会出现 crash 等情况对 APP 稳定性造成影响。
所以,使用时对js文件的内容进行加密是必须的

JSPatch的使用

先去GitHub上下载源码,加入到工程中 ![image](/content/images/2016/01/QQ20160113-2-2x.png) JSPatch经过作者的多次优化,在不断完善的过程中还是保持了极小的代码量,只有一千多行 OC 和接近两百行 JS(PS:再次膜拜一下大神) 这里使用到了JavaScriptcore核心库,所以还需要在General的Linded Framework and Libraries添加JavaScriptcore.framework ![image](/content/images/2016/01/QQ20160113ww-2-2x.png)

首先在AppDelegate中添加JPEngine.h的头文件
然后添加下面两个方法:


// Library/Caches
#define FilePath ([[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil])

/**
 *  下载JSPatch
 */
-(void)loadJSPatch
{
    //使用AFNetWork下载在服务器的js文件
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    NSURL *URL = [NSURL URLWithString:@"http://blog.methodname.com/content/test.js"];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response)
          {
              NSURL *documentsDirectoryURL = FilePath;
              //保存到本地 Library/Caches目录下
              return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
          }
            completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error)
          {
              NSLog(@"File downloaded to: %@", filePath);
          }];
    [downloadTask resume];
  
}

/**
 *  运行下载的JS文件
 */
-(void)HSDevaluateScript
{
   
    //从本地获取下载的JS文件
    NSURL *p = FilePath;
    //获取内容
    NSString *js = [NSString stringWithContentsOfFile:[p.path stringByAppendingString:@"/test.js"] encoding:NSUTF8StringEncoding error:nil];
   
    //如果有内容
    if (js.length > 0)
    {
        //-------
        //在此处解密js内容
        //----
        
        
        //运行
        [JPEngine startEngine];
        [JPEngine evaluateScript:js];
    }

}
//还是在AppDelegate中下面的两个方法内,进行下载服务器的js文件和运行下载后的js文件
//加载配置
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//可以在这里设置一个条件,比如隔多久才去请求一次服务器看看有没有可以下载的文件
//以防止频繁请求
    [self loadJSPatch];
    
    return YES;
}
//应用程序进入活动状态时
- (void)applicationDidBecomeActive:(UIApplication *)application
{
    [self HSDevaluateScript];
}

以上在客户端的配置就完成了

然后就是最关键的,如何使用js来完成调用,替换,覆盖原来的代码

JSPatch OC-JS自动转换工具:https://github.com/bang590/JSPatchConvertor


如果不想去记它的语法,完全可以用这个工具来进行OC,JS的转换,工具也是原作者写的(PS:再次膜拜大神,虽然有些语法上的瑕疵,具体请看github上的wiki)



//在MainViewController的viewDidLoad方法中将self.view的背景颜色改为橙色
//-------OC代码
@implementation MainViewController
-(void)viewDidLoad
{
    [super viewDidLoad];
    [self.view setBackgroundColor:[UIColor orangeColor]];
}
//------转换后的JS代码

require('UIColor,UIViewController');//这里少了个UIViewController加上就好
defineClass('MainViewController', {
    viewDidLoad: function() {
        super.viewDidLoad();
        self.view().setBackgroundColor(UIColor.orangeColor());
    },
});


将这个文件,放在服务器上面,最好进行版本分类,这样在App下载的时候,就能根据当前的版本号来进行对应目录下的文件的下载例如:

AppName-->2.1-->jspatch.js
AppName-->2.2-->jspatch.js
AppName-->2.3-->jspatch.js

这样就能在App上根据目录:AppName(当前App版本号)\jspatch.js 这样的方式去下载对应版本的js文件了


注意事项:

引用某位使用过JSPatch大神博客里最后的话 http://www.cnblogs.com/dsxniubility/p/5080875.html

1、接入了JSPatch之后,iOS的线上BUG 看上去就不向以前那样“猛如虎”了,但是这仅仅是一个紧急预案措施,以前规范的流程还是需要遵守。

2、每一次本版本用JSPatch解决的线上Bug,下个版本必须用OC代码写入项目中,不能允许补丁代码的存留超过一个版本。

3、倡导使用敏捷开发的思想,类似于主逻辑或者是功能模块入口的方法可以抽的更细,这样即使需要修改,成本也不会太大,作者本人也提到,如果有一行代码必须要在一个大方法的中间进行修改,那我也没办法了,你只能把这整个方法都用js写一遍了,所以才设置了JSPatchConvertor。

4、每次用JSPatch解决掉的线上BUG 应当有一个专门的文档记录,遇到重复错误必须写casestudy。

5、具体的使用请查看github上的wiki JSPatch wiki

最后:放上JSPatch的官网

目前jspatch的作者已经将jspatch商业化,并提供了完善的版本控制,脚本下发SDK,集成到APP中也只需要一行代码。
JSPatch


END