项目作者: bang590

项目描述 :
JSPatch使用Objective-C运行时桥接Objective-C和Javascript。您可以通过包含一个小引擎来调用JavaScript中的任何Objective-C类和方法。 JSPatch通常用于修复iOS App。
高级语言: Objective-C
项目地址: git://github.com/bang590/JSPatch.git
创建时间: 2015-05-25T02:37:22Z
项目社区:https://github.com/bang590/JSPatch

开源协议:MIT License

下载


JSPatch

Travis
CocoaPods Version
License

中文介绍 | 文档 | JSPatch平台

请大家不要自行接入 JSPatch,统一接入 JSPatch 平台,让热修复在一个安全和可控的环境下使用。原因详见 这里

JSPatch bridges Objective-C and JavaScript using the Objective-C runtime. You can call any Objective-C class and method in JavaScript by just including a small engine. That makes the APP obtaining the power of script language: add modules or replacing Objective-C code to fix bugs dynamically.

JSPatch is still in development, welcome to improve the project together.

Notice: Please go to Wiki to get full document.

Example

  1. @implementation AppDelegate
  2. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  3. {
  4. [JPEngine startEngine];
  5. NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
  6. NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
  7. [JPEngine evaluateScript:script];
  8. self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  9. [self.window addSubview:[self genView]];
  10. [self.window makeKeyAndVisible];
  11. return YES;
  12. }
  13. - (UIView *)genView
  14. {
  15. return [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
  16. }
  17. @end
  1. // demo.js
  2. require('UIView, UIColor, UILabel')
  3. defineClass('AppDelegate', {
  4. // replace the -genView method
  5. genView: function() {
  6. var view = self.ORIGgenView();
  7. view.setBackgroundColor(UIColor.greenColor())
  8. var label = UILabel.alloc().initWithFrame(view.frame());
  9. label.setText("JSPatch");
  10. label.setTextAlignment(1);
  11. view.addSubview(label);
  12. return view;
  13. }
  14. });

You can also try to use JSPatch Convertor to convertor code from Objective-C to JavaScript automatically.

Installation

CocoaPods

CocoaPods is a dependency manager for Objective-C, which automates and simplifies the process of using 3rd-party libraries like JSPatch in your projects. See the “Getting Started” guide for more information.

  1. # Your Podfile
  2. platform :ios, '6.0'
  3. pod 'JSPatch'

Manually

Copy JSEngine.m JSEngine.h JSPatch.js in JSPatch/ to your project.

Usage

Objective-C

  1. #import "JPEngine.h"
  2. call [JPEngine startEngine]
  3. exec JavasScript by [JPEngine evaluateScript:@""]
  1. [JPEngine startEngine];
  2. // exec js directly
  3. [JPEngine evaluateScript:@"\
  4. var alertView = require('UIAlertView').alloc().init();\
  5. alertView.setTitle('Alert');\
  6. alertView.setMessage('AlertView from js'); \
  7. alertView.addButtonWithTitle('OK');\
  8. alertView.show(); \
  9. "];
  10. // exec js file from network
  11. [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cnbang.net/test.js"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
  12. NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  13. [JPEngine evaluateScript:script];
  14. }];
  15. // exec local js file
  16. NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"js"];
  17. NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
  18. [JPEngine evaluateScript:script];

JavaScript

Base Usage

  1. //require
  2. require('UIView, UIColor, UISlider, NSIndexPath')
  3. // Invoke class method
  4. var redColor = UIColor.redColor();
  5. // Invoke instance method
  6. var view = UIView.alloc().init();
  7. view.setNeedsLayout();
  8. // set proerty
  9. view.setBackgroundColor(redColor);
  10. // get property
  11. var bgColor = view.backgroundColor();
  12. // multi-params method (use underline to separate)
  13. // OC:NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
  14. var indexPath = NSIndexPath.indexPathForRow_inSection(0, 1);
  15. // method name contains underline (use double undeline to represent)
  16. // OC: [JPObject _privateMethod];
  17. JPObject.__privateMethod()
  18. // use .toJS() to convert NSArray / NSString / NSDictionary to JS type.
  19. var arr = require('NSMutableArray').alloc().init()
  20. arr.addObject("JS")
  21. jsArr = arr.toJS()
  22. console.log(jsArr.push("Patch").join('')) //output: JSPatch
  23. // use hashes to represent struct like CGRect / CGSize / CGPoint / NSRange
  24. var view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100});
  25. var x = view.bounds().x;
  26. // wrap function with `block()` when passing block from JS to OC
  27. // OC Method: + (void)request:(void(^)(NSString *content, BOOL success))callback
  28. require('JPObject').request(block("NSString *, BOOL", function(ctn, succ) {
  29. if (succ) log(ctn)
  30. }));
  31. // GCD
  32. dispatch_after(1.0, function(){
  33. // do something
  34. })
  35. dispatch_async_main(function(){
  36. // do something
  37. })

Go to wiki page for more details: Base Usage

defineClass

You can redefine an existing class and override methods.

  1. // OC
  2. @implementation JPTableViewController
  3. ...
  4. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  5. {
  6. NSString *content = self.dataSource[[indexPath row]]; //may cause out of bound
  7. JPViewController *ctrl = [[JPViewController alloc] initWithContent:content];
  8. [self.navigationController pushViewController:ctrl];
  9. }
  10. - (NSArray *)dataSource
  11. {
  12. return @[@"JSPatch", @"is"];
  13. }
  14. - (void)customMethod
  15. {
  16. NSLog(@"callCustom method")
  17. }
  18. @end
  1. // JS
  2. defineClass("JPTableViewController", {
  3. // instance method definitions
  4. tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
  5. var row = indexPath.row()
  6. if (self.dataSource().count() > row) { //fix the out of bound bug here
  7. var content = self.dataSource().objectAtIndex(row);
  8. var ctrl = JPViewController.alloc().initWithContent(content);
  9. self.navigationController().pushViewController(ctrl);
  10. }
  11. },
  12. dataSource: function() {
  13. // get the original method by adding prefix 'ORIG'
  14. var data = self.ORIGdataSource().toJS();
  15. return data.push('Good!');
  16. }
  17. }, {})

Go to wiki page for more details: Usage of defineClass

Extensions

There are some extensions provide support for custom struct type, C methods and other functional, call +addExtensions: after starting engine to add extensions:

  1. @implementation AppDelegate
  2. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  3. {
  4. [JPEngine startEngine];
  5. //add extensions after startEngine
  6. [JPEngine addExtensions:@[@"JPInclude", @"JPCGTransform"]];
  7. NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
  8. NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
  9. [JPEngine evaluateScript:script];
  10. }
  11. @end
  1. include('test.js') //include function provide by JPInclude.m
  2. var view = require('UIView').alloc().init()
  3. //CGAffineTransform is supported in JPCGTransform.m
  4. view.setTransform({a:1, b:0, c:0, d:1, tx:0, ty:100})

Extensions can be added dynamiclly in JS, which is recommended:

  1. require('JPEngine').addExtensions(['JPInclude', 'JPCGTransform'])
  2. // `include()` and `CGAffineTransform` is avaliable now.

You can create your own extension to support custom struct type and C methods in project, see the wiki page for more details: Adding new extensions

Enviroment

  • iOS 7+, forward compatibility with iOS 6
  • JavaScriptCore.framework
  • Support armv7/armv7s/arm64