iOS-JSPatch热修复

作者: FlyElephant | 来源:发表于2017-06-02 20:29 被阅读93次

    iOS热更新绕过了苹果的审核,之前有些开发者收到了警告邮件,最新通知显示如果App中包含热更新不作调整,有可能下架.JSPatch最开始用于修复严重的线上bug,后来发展为修改创建各种模块,导致偏离了最开始的初衷.

    JSPatch

    JSPatch源码在GitHub上面托管,可以直接拖入工程中,也可以通过CocoaPods导入:

    pod 'JSPatch'
    

    假设项目中某个按钮点击之后执行代码过程中发生了数组越界:

    @interface ViewController ()
    
    @property (strong, nonatomic) NSMutableArray *data;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        self.data = [[NSMutableArray alloc] init];
        [self.data addObject:@"FlyElephant"];
        [self.data addObject:@"Keso"];
        [self.data addObject:@"北京"];
    }
    
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (IBAction)testAction:(UIButton *)sender {
        NSInteger index = 10;
        NSString *name = self.data[index];
        NSLog(@"数组%@",name);
    }
    
    @end
    

    如果这个场景发生在生产环境中,开发人员要么重新打包上线,要么对天祈祷,JSPatch提供了第三种可能性,方法替换:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        [JPEngine startEngine];
        NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"js"];
        NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
        [JPEngine evaluateScript:script];
        
        return YES;
    }
    

    main.js中替换方法的代码:

    require("NSMutableArray");
    
    defineClass("ViewController", {
                testAction: function(sender) {
                var index = 0;
                var tempData = self.data().toJS();
                if (index >= 0 && index < tempData.length) {
                var name = tempData[index];
                console.log("数组:"+name);
                } else {
                console.log("索引数据不存在")
                }
                }
                }, {});
    

    实现起来比较简单,这个实现我们只能实现本地替换,为了做到线上环境实时动态替换,就需要了解JSPatch热更新平台.

    JSPatch 服务平台

    Github 开源的是 JSPatch 核心代码,使用完全免费自由,若打算自己搭建后台下发 JSPatch 脚本,可以直接使用 github 上的核心代码,与 JSPatch 平台上的 SDK 无关。JSPatch 平台的 SDK 在核心代码的基础上增加了向平台请求脚本/传输解密/版本管理等功能,只用于这个平台。

    如果公司体量足够完成有实力自己实现JSPatch的后台,定制自己需要的功能,出于学习目的可以注册JSPatch服务平台的账号.

    按照官方指导文档,新建App

    FlyElephant.png

    生成公钥和私钥,mac终端执行openssl,执行以下命令:

    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
    

    上传修复Bug的JS文件和私钥:

    FlyElephant.png

    App启动设置:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        [JSPatch startWithAppKey:@"AppKey"];
        [JSPatch setupRSAPublicKey:@"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDN5NdQktnZ1pL8P1IXV2vJ8x+G\nSGORWCwjjUfnUwZD/bJust7JGvTXE/rt/AlnEFm\nR5U7d+7HEjI3gS14tR3ZGG/li83wmIvnMxGXfjwWQbN+EKoR7IGG6LDilqC+qDM3\nLPSwr2zkJtgsf4L2JQIDAQAB\n-----END PUBLIC KEY-----"];
        [JSPatch sync];
        return YES;
    }
    

    如果希望及时修复可DidBecomeActive唤醒App的时候同步JSPatch补丁:

    - (void)applicationDidBecomeActive:(UIApplication *)application {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
        [JSPatch sync];
    }
    

    测试代码:

    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        self.data = [[NSMutableArray alloc] init];
        [self.data addObject:@"FlyElephant"];
        [self.data addObject:@"Keso"];
        [self.data addObject:@"北京"];
    }
    
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    - (IBAction)testAction:(UIButton *)sender {
        NSInteger index = 10;
        NSString *name = self.data[index];
        NSLog(@"数组%@",name);
    }
    
    @end
    

    同步更新热修复文件在Librayw文件夹下面:

    Snip20170602_5.png

    Swift

    Swift实现方法交换需要在方法前面加入dynamic,同时需要类集成NSObject.
    定义User类:

    open class User:NSObject {
        
        dynamic func buyCourse() {
            print("FlyElephant---通过Swift购买图书")
        }
    }
    

    购买事件:

       @IBAction func buyAction(_ sender: UIButton) {
            
            let user:User = User()
            
            user.buyCourse()
        }
    

    main.js 设置:

    defineClass('FESwiftDemo.User', {
                buyCourse: function() {
                console.log('FlyElephant---通过JS购买课程')
                }
                })
    

    App 启动设置:

       func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
            // Override point for customization after application launch.
            
            let path = Bundle.main.path(forResource: "main", ofType: "js")
            do {
                let patch = try String(contentsOfFile: path!)
                
                JPEngine.start()
                JPEngine.evaluateScript(patch)
                
            } catch {}
            return true
        }
    

    参考链接:
    JSPatch源码
    JSPatch服务平台
    Swift方法交换

    相关文章

      网友评论

        本文标题:iOS-JSPatch热修复

        本文链接:https://www.haomeiwen.com/subject/kroyfxtx.html