Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE.MIT
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 DouKing <wyk8916@gmail.com>
Copyright (c) 2021-2025 DouKing (https://github.com/DouKing/)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.NPL
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2019 DouKing <wyk8916@gmail.com>
Copyright (c) 2021-2025 DouKing (https://github.com/DouKing/)

"Anti 996" License Version 1.0 (Draft)

Expand Down
40 changes: 33 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,23 @@

# STMScriptMessageHandler

![capture](./Capture.gif)
<img src="./Capture.gif" style="float:right">

The `STMScriptMessageHandler` is used to comunicate with js for `WKWebView`. It implements `WKScriptMessageHandler` and `WKScriptMessageHandlerWithReply` protocol. A `STMScriptMessageHandler` corresponding a js object. When your `WKWebView` add a `STMScriptMessageHandler`, the js side add a object on the window automatically. The handlerName of `STMScriptMessageHandler` is the js object's name.

The `STMScriptMessageHandler` is used to comunicate with js for `WKWebView`. It implements `WKScriptMessageHandler` protocol. A `STMScriptMessageHandler` corresponding a js object. When your `WKWebView` add a `STMScriptMessageHandler`, the js side add a object automatically. The handlerName of `STMScriptMessageHandler` is the js object's name.
## Features

- [x] Support multi message handlers
- [x] Support Promise
- [x] Compatible with `WebViewJavascriptBridge`

## Requirements

iOS 8.0+

## Usage

- Native side
#### Native side

```objectivec

Expand All @@ -33,20 +37,42 @@ STMScriptMessageHandler *page = [[STMScriptMessageHandler alloc] initWithScriptM

```

- JS side
#### JS side

```javascript
// Use js object `window.Bridge` call native method or register method for native.
window.Bridge.callMethod('testNativeMethod', {foo:'foo1', bar: 'bar1'}, function(data){
log('JS got native `testNativeMethod` response', data);
window.Bridge.callHandler('nslog', data, function (data) {
log('JS got native `nslog` response', data);
});

window.Bridge.registerMethod('log', function(data, callback){
var message = JSON.parse(data);
log('Native calling js method `log`', message);
callback({key: 'from js', value: 'something'});
});


// Support Promise
async function promise(data) {
// window.Bridge.callHandler('nslog', data).then(
// result => log('JS got native `nslog` response', result),
// error => log('JS got native `nslog` error', error)
// )

var p = window.Bridge.callHandler('nslog', data);
var result = await p;
log('JS got native `nslog` response', result);
}
```

#### Migrate from `WebViewJavascriptBridge`

If you register a message handler named `WebViewJavascriptBridge` at native side, the js side dones not need modify any code.

```objectivec

// The bridge's name is `WebViewJavascriptBridge`
self.bridge = [self.webView stm_addScriptMessageHandlerUseName:@"WebViewJavascriptBridge"];

```

## Installation
Expand Down
2 changes: 2 additions & 0 deletions STMScriptMessageHandler.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RQW526M45R;
INFOPLIST_FILE = STMScriptMessageHandler/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = (
Expand All @@ -344,6 +345,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = RQW526M45R;
INFOPLIST_FILE = STMScriptMessageHandler/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down
1 change: 1 addition & 0 deletions STMScriptMessageHandler/Demo/STMViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ @implementation STMViewController

- (void)viewDidLoad {
[super viewDidLoad];
[STMScriptMessageHandler enableLog];
[self prepareScriptMessageHandler];
NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
[self.webView loadHTMLString:[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil] baseURL:nil];
Expand Down
184 changes: 110 additions & 74 deletions STMScriptMessageHandler/Demo/index.html
Original file line number Diff line number Diff line change
@@ -1,75 +1,111 @@
<!doctype html>
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<title>Demo</title>
<style type='text/css'>
html { font-family:Helvetica; color:#222; }
h1 { color:steelblue; font-size:24px; margin-top:24px; }
button { margin:0 3px 10px; font-size:12px; }
.logLine { border-bottom:1px solid #ccc; padding:4px 2px; font-family:courier; font-size:11px; }
</style><script language="javascript">
var uniqueId = 1;
var testCallback;

var JSBridge = window.Bridge;
var JSPage = window.Page;

function log(message, data) {
var log = document.getElementById('log')
var el = document.createElement('div')
el.className = 'logLine'
el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
if (log.children.length) { log.insertBefore(el, log.children[0]) }
else { log.appendChild(el) }
}

function setRightButtons() {
var buttons = [{ "title": '按钮0' }, { "title": '按钮1' }];
log('JS calling native method "setButtons"', buttons);
JSPage.callHandler('setButtons', {'callback': 'handleTapButtonAction', 'data': buttons}, function (result) {
log('JS got native `setButtons` response', result);
});
}

function nslog(data) {
JSBridge.callHandler('nslog', data, function (data) {
log('JS got native `nslog` response', data);
});
}

function test(data) {
if (testCallback) {
log('calling callback', data);
testCallback(data);
} else {
log('callback is undefined');
}
}

JSBridge.callHandler('testNativeMethod', { foo: 'foo', bar: 'bar' }, function (data) {
log('JS got native `testNativeMethod` response', data);
});

JSBridge.registerHandler('log', function (data, callback) {
var message = data;
log('Native calling js method `log`', message);
callback({ key: 'from js', value: data });
});

JSBridge.registerHandler('test', function(data, callback) {
log('save callback');
testCallback = callback;
});

JSPage.registerHandler('handleTapButtonAction', function(index) {
var response = '点击了按钮' + index;
log('Native calling js method `handleTapButtonAction`', response);
});

</script>
</head><body>
<input type="button" value="Set Right Buttons" onclick="setRightButtons()" />
<input type="button" value="nslog" onclick="nslog({'foo': 'foo'})" />
<input type="button" value="test callback" onclick="test({'key': 'value'})" />
<div id='log'></div>
</body></html>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<title>Demo</title>
<style type='text/css'>
html {
font-family: Helvetica;
color: #222;
}

h1 {
color: steelblue;
font-size: 24px;
margin-top: 24px;
}

button {
margin: 0 3px 10px;
font-size: 12px;
}

.logLine {
border-bottom: 1px solid #ccc;
padding: 4px 2px;
font-family: courier;
font-size: 11px;
}
</style>
<script language="javascript">
var uniqueId = 1;
var testCallback;

var JSBridge = window.Bridge;
var JSPage = window.Page;

function log(message, data) {
var log = document.getElementById('log')
var el = document.createElement('div')
el.className = 'logLine'
el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
if (log.children.length) { log.insertBefore(el, log.children[0]) }
else { log.appendChild(el) }
}

function setRightButtons() {
var buttons = [{ "title": '按钮0' }, { "title": '按钮1' }];
log('JS calling native method "setButtons"', buttons);
JSPage.callHandler('setButtons', { 'callback': 'handleTapButtonAction', 'data': buttons }, function (result) {
log('JS got native `setButtons` response', result);
});
}

function nslog(data) {
JSBridge.callHandler('nslog', data, function (data) {
log('JS got native `nslog` response', data);
});
}

async function promise(data) {
// JSBridge.callHandler('nslog', data).then(
// result => log('JS got native `nslog` response', result),
// error => log('JS got native `nslog` error', error)
// )

var p = JSBridge.callHandler('nslog', data);
var result = await p;
log('JS got native `nslog` response', result);
}

function test(data) {
if (testCallback) {
log('calling callback', data);
testCallback(data);
} else {
log('callback is undefined');
}
}

JSBridge.callHandler('testNativeMethod', { foo: 'foo', bar: 'bar' }, function (data) {
log('JS got native `testNativeMethod` response', data);
});

JSBridge.registerHandler('log', function (data, callback) {
var message = data;
log('Native calling js method `log`', message);
callback({ key: 'from js', value: data });
});

JSBridge.registerHandler('test', function (data, callback) {
log('save callback');
testCallback = callback;
});

JSPage.registerHandler('handleTapButtonAction', function (index) {
var response = '点击了按钮' + index;
log('Native calling js method `handleTapButtonAction`', response);
});
</script>
</head>

<body>
<input type="button" value="Set Right Buttons" onclick="setRightButtons()" />
<input type="button" value="nslog" onclick="nslog({'foo': 'foo'})" />
<input type="button" value="promise" onclick="promise({'bar': 'bar'})" />
<input type="button" value="test callback" onclick="test({'key': 'value'})" />
<div id='log'></div>
</body>

</html>
25 changes: 22 additions & 3 deletions STMScriptMessageHandler/Source/STMScriptMessageHandler.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
//
// STMScriptMessageHandler.h
// Pods-STMWebViewController_Example
//
// Created by DouKing on 2018/7/31.
// Copyright (c) 2021-2025 DouKing (https://github.com/DouKing/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

@import UIKit;
Expand All @@ -13,10 +30,12 @@ NS_ASSUME_NONNULL_BEGIN
typedef void (^STMResponseCallback)(id responseData);
typedef void (^STMHandler)(id data, STMResponseCallback _Nullable responseCallback);

@interface STMScriptMessageHandler : NSObject<WKScriptMessageHandler>
@interface STMScriptMessageHandler : NSObject<WKScriptMessageHandler, WKScriptMessageHandlerWithReply>

@property (nonatomic, copy, readonly) NSString *handlerName;

+ (void)enableLog;

+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithScriptMessageHandlerName:(NSString *)handlerName forWebView:(WKWebView *)webView;
Expand Down
Loading