JSPatch bridge 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. JSPatch is generally used to hotfix iOS App.

Overview

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

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
    [JPEngine startEngine];
    NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
    [JPEngine evaluateScript:script];
    
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [self.window addSubview:[self genView]];
    [self.window makeKeyAndVisible];
    
    return YES;
}

- (UIView *)genView
{
    return [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 320)];
}

@end
// demo.js
require('UIView, UIColor, UILabel')
defineClass('AppDelegate', {
  // replace the -genView method
  genView: function() {
    var view = self.ORIGgenView();
    view.setBackgroundColor(UIColor.greenColor())
    var label = UILabel.alloc().initWithFrame(view.frame());
    label.setText("JSPatch");
    label.setTextAlignment(1);
    view.addSubview(label);
    return view;
  }
});

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.

# Your Podfile
platform :ios, '6.0'
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:@""]
[JPEngine startEngine];

// exec js directly
[JPEngine evaluateScript:@"\
 var alertView = require('UIAlertView').alloc().init();\
 alertView.setTitle('Alert');\
 alertView.setMessage('AlertView from js'); \
 alertView.addButtonWithTitle('OK');\
 alertView.show(); \
"];

// exec js file from network
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://cnbang.net/test.js"]] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
    NSString *script = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    [JPEngine evaluateScript:script];
}];

// exec local js file
NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"js"];
NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
[JPEngine evaluateScript:script];

JavaScript

Base Usage

//require
require('UIView, UIColor, UISlider, NSIndexPath')

// Invoke class method
var redColor = UIColor.redColor();

// Invoke instance method
var view = UIView.alloc().init();
view.setNeedsLayout();

// set proerty
view.setBackgroundColor(redColor);

// get property 
var bgColor = view.backgroundColor();

// multi-params method (use underline to separate)
// OC:NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
var indexPath = NSIndexPath.indexPathForRow_inSection(0, 1);

// method name contains underline (use double undeline to represent)
// OC: [JPObject _privateMethod];
JPObject.__privateMethod()

// use .toJS() to convert NSArray / NSString / NSDictionary to JS type.
var arr = require('NSMutableArray').alloc().init()
arr.addObject("JS")
jsArr = arr.toJS()
console.log(jsArr.push("Patch").join(''))  //output: JSPatch

// use hashes to represent struct like CGRect / CGSize / CGPoint / NSRange
var view = UIView.alloc().initWithFrame({x:20, y:20, width:100, height:100});
var x = view.bounds().x;

// wrap function with `block()` when passing block from JS to OC
// OC Method: + (void)request:(void(^)(NSString *content, BOOL success))callback
require('JPObject').request(block("NSString *, BOOL", function(ctn, succ) {
  if (succ) log(ctn)
}));

// GCD
dispatch_after(1.0, function(){
  // do something
})
dispatch_async_main(function(){
  // do something
})

Go to wiki page for more details: Base Usage

defineClass

You can redefine an existing class and override methods.

// OC
@implementation JPTableViewController
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  NSString *content = self.dataSource[[indexPath row]];  //may cause out of bound
  JPViewController *ctrl = [[JPViewController alloc] initWithContent:content];
  [self.navigationController pushViewController:ctrl];
}
- (NSArray *)dataSource
{
  return @[@"JSPatch", @"is"];
}
- (void)customMethod
{
  NSLog(@"callCustom method")
}
@end
// JS
defineClass("JPTableViewController", {
  // instance method definitions
  tableView_didSelectRowAtIndexPath: function(tableView, indexPath) {
    var row = indexPath.row()
    if (self.dataSource().count() > row) {  //fix the out of bound bug here
      var content = self.dataSource().objectAtIndex(row);
      var ctrl = JPViewController.alloc().initWithContent(content);
      self.navigationController().pushViewController(ctrl);
    }
  },

  dataSource: function() {
    // get the original method by adding prefix 'ORIG'
    var data = self.ORIGdataSource().toJS();
    return data.push('Good!');
  }
}, {})

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:

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
{
    [JPEngine startEngine];

    //add extensions after startEngine
    [JPEngine addExtensions:@[@"JPInclude", @"JPCGTransform"]];

    NSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    NSString *script = [NSString stringWithContentsOfFile:sourcePath encoding:NSUTF8StringEncoding error:nil];
    [JPEngine evaluateScript:script];
}

@end
include('test.js')   //include function provide by JPInclude.m
var view = require('UIView').alloc().init()

//CGAffineTransform is supported in JPCGTransform.m
view.setTransform({a:1, b:0, c:0, d:1, tx:0, ty:100})

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

require('JPEngine').addExtensions(['JPInclude', 'JPCGTransform'])

// `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
Comments
  • Apple警告邮件

    Apple警告邮件

    统一回复:关于苹果警告 http://blog.cnbang.net/internet/3374/

    @bang590 的回复


    今天收到Apple的警告邮件。 应用中使用了JSPatch一段时间了,之前的版本是没有问题的。 而且这个通知邮件也不是在提交更新版本审核过程中收到,而是苹果主动发出的。

    Dear Developer,

    Your app, extension, and/or linked framework appears to contain code designed explicitly with the capability to change your app’s behavior or functionality after App Review approval, which is not in compliance with section 3.3.2 of the Apple Developer Program License Agreement and App Store Review Guideline 2.5.2. This code, combined with a remote resource, can facilitate significant changes to your app’s behavior compared to when it was initially reviewed for the App Store. While you may not be using this functionality currently, it has the potential to load private frameworks, private methods, and enable future feature changes.

    This includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior or call SPI, based on the contents of the downloaded script. Even if the remote resource is not intentionally malicious, it could easily be hijacked via a Man In The Middle (MiTM) attack, which can pose a serious security vulnerability to users of your app.

    Please perform an in-depth review of your app and remove any code, frameworks, or SDKs that fall in line with the functionality described above before submitting the next update for your app for review.

    Best regards,

    App Store Review


    no-mark unsubscribe
    opened by kunwang0916 620
  • 可能用到patch相关的第三方SDK——处理进度汇总

    可能用到patch相关的第三方SDK——处理进度汇总

    bug tags 有   已解决(新的SDK已发布) 个推   有 尚未解决 有临时方案解决(详情咨询官方客服) 友盟统计   无 bugly   崩溃统计 无 热修复 有 美洽 无

    ——————————————欢迎大家补充————————————

    opened by niukaiquan 29
  • 最新消息, 我被拒了,直接拒绝,新版本也不行了?

    最新消息, 我被拒了,直接拒绝,新版本也不行了?

    Guideline 2.5.2 - Performance

    Your app, extension, or linked framework appears to contain code designed explicitly with the capability to change your app’s behavior or functionality after App Review approval, which is not in compliance with App Store Review Guideline 2.5.2 and section 3.3.2 of the Apple Developer Program License Agreement.

    This code, combined with a remote resource, can facilitate significant changes to your app’s behavior compared to when it was initially reviewed for the App Store. While you may not be using this functionality currently, it has the potential to load private frameworks, private methods, and enable future feature changes. This includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior and/or call SPI, based on the contents of the downloaded script. Even if the remote resource is not intentionally malicious, it could easily be hijacked via a Man In The Middle (MiTM) attack, which can pose a serious security vulnerability to users of your app.

    opened by LuYu001 21
  • 如何在js中使用CLLocationCoordinate2D呀?

    如何在js中使用CLLocationCoordinate2D呀?

    我想在js中覆盖一个调起百度地图的方法,方法体内用到坐标转换,需要将这个 CLLocationCoordinate2D 的坐标点传进去,不知道该怎么用这个结构体,我直接传{latitude:39.979385, longitude:116.476723}这个的话,坐标转换方法 读到的是{latitude:0,longitude:0}。请大神指教~ 附代码: var startCenter = require('JZLocationConverter').gcj02ToBd09({latitude:39.979385, longitude:116.476723}) console.log("startCenter ===="+startCenter); // startCenter 打印出来是null

    opened by chiefky 20
  • 加入了JSPatch 对可变参数的支持

    加入了JSPatch 对可变参数的支持

    通过objc_msgSend 方法来实现可变参数的传入,暂时只支持10个参数以下的函数参数,以后可能会改成更优雅的写法 😊 Example js:

    var alertView = require('UIAlertView').alloc().initWithTitle_message_delegate_cancelButtonTitle_otherButtonTitles("Alert",self.dataSource().objectAtIndex(indexPath.row()), self, require('NSString').stringWithFormat("%@ %@", "a", "b"), "Cancel", null); alertView.show();

    另外还需要Do more testing :-)

    opened by wjacker 19
  • forward compatibility and best cocoapods practice

    forward compatibility and best cocoapods practice

    支持iOS 6支持iOS 6支持iOS 6最近流行重要的事情说三遍

    需要部署在iOS 6的target,应该对JavascriptCore进行weak linking,之后在runtime判断JavascriptCore是否可用(这里用的是![JSContext class]进行判断,在weak linking的前提下不会crash),当然事实上是可用的,因为会链接到私有的JavascriptCore,根据使用Webkit框架的经验,苹果的审核政策可以接受先前兼容

    此外根据stackoverflow上的介绍,还可以通过指定链接标识来不明确链接到JavascriptCore -Wl,-U,_JSGlobalContextCreate -Wl,-U,_JSGlobalContextRelease 在App加载时能通过UIKit → WebKit → JavaScriptCore这条依赖路径完成JavascriptCore的链接

    不过我觉得没卵用

    opened by yirenjun 17
  • protocol的conform定义问题

    protocol的conform定义问题

    你好bang590,使用defineProtocol可以定义单一协议,使用没有问题。 例如: defineProtocol('JPDemoProtocol',{ test: { paramsType:"", returnType:"", }, }) defineClass('NewJSObject:NSObject', { test: function() { console.log('JS New log'); }, test1: function() { console.log('JS New log1'); } }) 但是,如果使用协议来conform已有协议,则会出现崩溃,想确认下是不是我写错了,谢谢: defineProtocol('JPDemoProtocol2',{ test1: { paramsType:"", returnType:"", }, })

    defineProtocol('JPDemoProtocol',{ test: { paramsType:"", returnType:"", }, })

    defineClass('NewJSObject:NSObject', { test: function() { console.log('JS New log'); }, test1: function() { console.log('JS New log1'); } }) image

    opened by chesterlee 16
  • 如何在js中使用OC中已有的宏呀?

    如何在js中使用OC中已有的宏呀?

    下面是wiki上介绍宏的用法,没太看懂,谁能帮忙解释一下,跪求。

    Objective-C 里的宏同样不能直接在 JS 上使用,若定义的宏是一个值,可以在 JS 定义同样的全局变量代替,若定义的宏是程序,或者在底层才能获取到的值,例如 CGFLOAT_MIN,可以通过添加扩展的方式提供支持:

    @implementation JPMacroSupport

    • (void)main:(JSContext *)context { context[@"CGFLOAT_MIN"] = ^CGFloat() { return CGFLOAT_MIN; } }

    @end

    require('JPEngine').addExtensions(['JPMacroSupport']) var floatMin = CGFLOAT_MIN();

    opened by whihail 16
  • block 赋值

    block 赋值

    image NCDFNewStoreDataOperator 这个类里面有一success_block这样的block,现在我用JS修改他,修改好的代码如下 image 用OC代码写的就能在NCDFNewStoreDataOperator这个类中控制台输出success_block,但是用JS代码更改过的输出的就是null。

    这个应该怎么解决呢?

    opened by jianweihehe 15
  • imageWithCGImage方法,调用就闪退

    imageWithCGImage方法,调用就闪退

    使用版本:JSPatch 0.1.5 调用语句: UIImage *fullResolutionImage = [UIImage imageWithCGImage:fullResolutionImageRef scale:1.0 orientation:UIImageOrientationUp];

    var fullResolutionImage = require('UIImage').imageWithCGImage_scale_orientation(fullResolutionImageRef,1.0,0);

    执行后在JPEngine 899行[invocation invoke]; 发生BAD_ACCESS

    opened by summertian4 14
  • 使用aspects时 如果controller自己实现了forwardInvocation不崩溃

    使用aspects时 如果controller自己实现了forwardInvocation不崩溃

    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        [super forwardInvocation:anInvocation];
    }
    
    注掉就崩溃,是不是能查查原因?
    

    NSMethodSignature *methodSignature = [slf methodSignatureForSelector:origForwardSelector]; NSInvocation *forwardInv= [NSInvocation invocationWithMethodSignature:methodSignature]; 被打补丁对象如果自己实现了forwardInvocation,那么这里的执行是正确的,没有实现就崩溃了 是不是说明methodSignature没有获取到NSObject中相关的方法,如何能获取NSObject的呢?

    opened by askday 13
  • 按照开发文档一步一步集成, 没报错误,也提示新的补丁请求成功,但是页面上UI没有改变。

    按照开发文档一步一步集成, 没报错误,也提示新的补丁请求成功,但是页面上UI没有改变。

    按照开发文档一步一步集成, 没报错误,也提示新的补丁请求成功,但是页面上UI没有改变, 我用的 pod 'JSPatchPlatform', '~> 1.6.6' 。 是不是最新版本sdk的问题吗 。 我看文档上说让直接下载sdk。然后拉近项目中。可是那个试用的我下载没反应。 请问这种问题怎么解决呢!

    下面是日志的输出

    2021-03-10 17:19:08.917979+0800 DemoPatch[75845:4288150] log = JSPatch: succ stat error, ret:(null) err:(null) data: k=4f3097a0ecb769dd&av=1.0.0&pv=3&d=94186BEA-2E04-42E6-AC97-9AE9C81511F5 2021-03-10 17:19:11.006162+0800 DemoPatch[75845:4288150] log = JSPatch: 请求补丁文件成功,大小:3072 2021-03-10 17:19:15.884859+0800 DemoPatch[75845:4288150] log = JSPatch: 更新补丁版本 4 成功 2021-03-10 17:19:17.869267+0800 DemoPatch[75845:4288150] -----type = 3 data = { version = 4; } error = (null) 2021-03-10 17:19:19.171951+0800 DemoPatch[75845:4288150] log = JSPatch: 尝试加载补丁 2021-03-10 17:19:21.725082+0800 DemoPatch[75845:4288150] log = JSPatch: 当前没有补丁

    opened by shimminZ 5
  • js 中block回调给OC崩溃

    js 中block回调给OC崩溃

    
    require("NSMutableDictionary, NSMutableArray, NSString, NSArray, NSArray, HttpTools,NSError");
    
    defineClass("TKInnerHomePageNet", {
        getInnerHomePageLayout: function(successBlock) {
            var temStr = "https://isaleuat.taikang.com/isales/homepage/layout/getHomepageLayout";
            var urlStr = temStr;
            var param = NSMutableDictionary.dictionary();
            param.setValue_forKey("内勤协同版", "appType");
            var sel = self;
            var temArray = NSMutableArray.array();
            temArray.addObject("1003");
            temArray.addObject("1004");
            temArray.addObject("1005");
            temArray.addObject("1006");
            console.log('这里走到了666');
            successBlock(temArray);
            console.log('这里走到了444');
           //打印到666就崩溃,传入数组是崩溃
        }
    }, {});
    /*
    //报错结果
    Printing description of log:
    js exception, 
    msg: Argument does not match Objective-C Class, 
    stack: 
     [native code]
    JSPatch.js:32:42
    [email protected]:94:21
    JSPatch.js:153:41
    */
    
    /*
       对应OC方法
    - (void)getInnerHomePageLayout:(void(^)(NSMutableArray *cellCodeArray))successBlock
    */
    
    opened by MaFeifly 0
  • 最新的代码有内存泄漏

    最新的代码有内存泄漏

    static id genCallbackBlock(JSValue *jsVal){

    const char *fs = [funcSignature UTF8String]; char *s = malloc(strlen(fs)); strcpy(s, fs); *signature = s;

    master分支代码 这个方法最后分配了内存没有地方释放

    opened by Richard-yhy 1
Owner
bang
bang
A framework for sharing code between iOS and OS X

MAIKit MAIKit (Mac and iOS Kit) is a framework for sharing code between OS X and iOS. UIKit contains many classes which have counterparts in Appkit. T

Michael Buckley 139 Jun 29, 2022
Simple Design for Swift bridge with Javascript. Also can get javascript console.log.

SDBridgeOC is here. If your h5 partner confused about how to deal with iOS and Android. This Demo maybe help. YouTube video is here. bilibili Video is

null 20 Dec 28, 2022
A fast, convenient and nonintrusive conversion framework between JSON and model. Your model class doesn't need to extend any base class. You don't need to modify any model file.

MJExtension A fast, convenient and nonintrusive conversion framework between JSON and model. 转换速度快、使用简单方便的字典转模型框架 ?? ✍??Release Notes: more details Co

M了个J 8.5k Jan 3, 2023
KeyPathKit is a library that provides the standard functions to manipulate data along with a call-syntax that relies on typed keypaths to make the call sites as short and clean as possible.

KeyPathKit Context Swift 4 has introduced a new type called KeyPath, with allows to access the properties of an object with a very nice syntax. For in

Vincent Pradeilles 406 Dec 25, 2022
KeyPathKit is a library that provides the standard functions to manipulate data along with a call-syntax that relies on typed keypaths to make the call sites as short and clean as possible.

KeyPathKit Context Swift 4 has introduced a new type called KeyPath, with allows to access the properties of an object with a very nice syntax. For in

Vincent Pradeilles 406 Dec 25, 2022
Collapse and expand UICollectionView sections with one method call.

This library provides a custom UICollectionView that allows to expand and collapse sections. Provides a simple API to manage collection view appearanc

Touchlane 172 Dec 26, 2022
An iOS/OSX bridge for sending messages between Obj-C and JavaScript in UIWebViews/WebViews

WebViewJavascriptBridge An iOS/OSX bridge for sending messages between Obj-C and JavaScript in WKWebViews, UIWebViews & WebViews. Migration Guide When

Marcus Westin 14.1k Jan 6, 2023
Runtime Mobile Security (RMS) 📱🔥 - is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime

Runtime Mobile Security (RMS) ?? ?? by @mobilesecurity_ Runtime Mobile Security (RMS), powered by FRIDA, is a powerful web interface that helps you to

Mobile Security 2k Dec 29, 2022
iOS app for Technex, IIT(BHU) Varanasi. This project is closed before completion. You can use this app for learning purpose. You can use this app as a templet of any event related app.

technex-ios iOS app for Technex, IIT(BHU) Varanasi. This project is closed before completion for some reasons. You can use this app for learning purpo

Jogendra 12 May 9, 2022
Make your table view expandable just by implementing one method.

ExpyTableView About ExpyTableView is a re-write based on SLExpandableTableView. Takes some ideas, concepts and codes and regenerates them in Swift. Le

Okhan Okbay 359 Dec 27, 2022
Varnam Input Method Engine for macOS. Easily type Indian languages on macOS

VarnamIME for macOS Easily type Indian languages on macOS using Varnam transliteration engine. Built at FOSSUnited's FOSSHack21. This project is a har

Varnamproject 11 Sep 7, 2022
Mathias Köhnke 1.1k Dec 16, 2022
Swift-compute-runtime - Swift runtime for Fastly Compute@Edge

swift-compute-runtime Swift runtime for Fastly Compute@Edge Getting Started Crea

Andrew Barba 57 Dec 24, 2022
MisterFusion is Swift DSL for AutoLayout. It is the extremely clear, but concise syntax, in addition, can be used in both Swift and Objective-C. Support Safe Area and Size Class.

MisterFusion MisterFusion makes more easier to use AutoLayout in Swift & Objective-C code. Features Simple And Concise Syntax Use in Swift and Objecti

Taiki Suzuki 316 Nov 17, 2022
You can use blur effect and it's animation easily to call only two methods.

SABlurImageView You can use blur effect and it's animation easily to call only two methods. ManiacDev.com referred. https://maniacdev.com/2015/04/open

Taiki Suzuki 555 Dec 17, 2022
Poi - You can use tinder UI like tableview method

Poi You can use tinder UI like tableview method Installation Manual Installation Use this command git clone [email protected]:HideakiTouhara/Poi.git Imp

null 65 Nov 7, 2022
A general purpose embedded hierarchical lock manager used to build highly concurrent applications of all types. Same type of locker used in many of the large and small DBMSs in existence today.

StickyLocking StickyLocking is a general purpose embedded lock manager which allows for locking any resource hierarchy. Installable Lock modes allow f

Sticky Tools 2 Jun 15, 2021
A simple and opinionated AES encrypt / decrypt Objective-C class that just works.

AESCrypt-ObjC - Simple AES encryption / decryption for iOS and OS X AESCrypt is a simple to use, opinionated AES encryption / decryption Objective-C c

Gurpartap Singh 782 Oct 12, 2022
Sample projects for using .NET and Swift with SourceGear Bridge

bridge-samples-swift This repo contains various sample projects which show using .NET and Swift with SourceGear Bridge. (Note that SourceGear Bridge i

SourceGear LLC 3 Nov 30, 2021