Cooooper/KKJSBridge
Built by Metorial, the integration platform for agentic AI.
Cooooper/KKJSBridge
Server Summary
Manage Ajax requests
Synchronize cookies
Support offline resources
Control Ajax hooks
Message handling
Modular JSAPI management
Share context information
Reuse WKWebView
Compatibility with WebViewJavascriptBridge
一站式解决 WKWebView 支持离线包,Ajax 请求和 Cookie 同步的问题 (基于 Ajax Hook 和 Cookie Hook)
基于 MessageHandler 搭建通信层
支持模块化的管理 JSAPI
支持模块共享上下文信息
支持模块消息转发
支持离线资源
支持 ajax hook 避免 body 丢失
Native 和 H5 侧都可以控制 ajax hook 开关
Cookie 统一管理
WKWebView 复用
兼容 WebViewJavascriptBridge
模块化调用 JSAPI
ajax hook 演示
淘宝 ajax hook 演示
从复用池取出缓存的 WKWebView,并开启 ajax hook
+ (void)load {
__block id observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
[self prepareWebView];
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}];
}
+ (void)prepareWebView {
// 预先缓存一个 webView
[KKWebView configCustomUAWithType:KKWebViewConfigUATypeAppend UAString:@"KKJSBridge/1.0.0"];
[[KKWebViewPool sharedInstance] enqueueWebViewWithClass:KKWebView.class];
}
- (void)dealloc {
// 回收到复用池
[[KKWebViewPool sharedInstance] enqueueWebView:self.webView];
}
- (void)commonInit {
_webView = [[KKWebViewPool sharedInstance] dequeueWebViewWithClass:KKWebView.class webViewHolder:self];
_webView.configuration.allowsInlineMediaPlayback = YES;
_webView.configuration.preferences.minimumFontSize = 12;
_webView.hybirdDelegate = self;
_jsBridgeEngine = [KKJSBridgeEngine bridgeForWebView:self.webView];
_jsBridgeEngine.config.enableAjaxHook = YES;
[self registerModule];
}
注册模块
- (void)registerModule {
ModuleContext *context = [ModuleContext new];
context.vc = self;
context.scrollView = self.webView.scrollView;
context.name = @"上下文";
// 注册 默认模块
[self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleDefault.class];
// 注册 模块A
[self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleA.class];
// 注册 模块B 并带入上下文
[self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleB.class withContext:context];
// 注册 模块C
[self.jsBridgeEngine.moduleRegister registerModuleClass:ModuleC.class];
}
模块定义
@interface ModuleB()
@property (nonatomic, weak) ModuleContext *context;
@end
@implementation ModuleB
// 模块名称
+ (nonnull NSString *)moduleName {
return @"b";
}
// 单例模块
+ (BOOL)isSingleton {
return YES;
}
// 模块初始化方法,支持上下文带入
- (instancetype)initWithEngine:(KKJSBridgeEngine *)engine context:(id)context {
if (self = [super init]) {
_context = context;
NSLog(@"ModuleB 初始化并带上 %@", self.context.name);
}
return self;
}
// 模块提供的方法
- (void)callToGetVCTitle:(KKJSBridgeEngine *)engine params:(NSDictionary *)params responseCallback:(void (^)(NSDictionary *responseData))responseCallback {
responseCallback ? responseCallback(@{@"title": self.context.vc.navigationItem.title ? self.context.vc.navigationItem.title : @""}) : nil;
}
JS 侧调用方式
window.KKJSBridge.call('b', 'callToGetVCTitle', {}, function(res) {
console.log('receive vc title:', res.title);
});