一、SignalR介绍
SignalR是微软基于.Net提供的一个开源实时Web RPC库,可以用在web实时通信的需求上面,比如聊天,web数据更新
SignalR的接口使用十分简单
由于最近的一个需求需要调研RPC库,偶然看到github上面一份在iOS上面使用SignalR 客户端请求的代码
下面对代码有意思的地方进行一下分析
二、专门去搜了一下,github上面存在一份400+赞的SignalR native代码,应该可以使用;
除此之外,工程代码:https://github.com/adamhartford/SwiftR 提供了另外一种方式
signalR在web上面应用比较成熟,因此jQuery上面有对应的lib,
这一份工程也是使用一个webview,运行本地的html文件,使用jQuery的signalR库,然后注入一段js,这段js跟本地app桥接通信
html文件:
<html>
<head></head>
<body>
"
<script src="\'file:///private/var/mobile/Containers/Data/Application/C5176D20-0015-4DFB-9156-D53DB6544F3E/tmp/SwiftR/jquery-2.1.3.min.js\'"></script>
<script src="\'file:///private/var/mobile/Containers/Data/Application/C5176D20-0015-4DFB-9156-D53DB6544F3E/tmp/SwiftR/jquery.signalr-2.2.0.min\'"></script>
<script src="\'file:///private/var/mobile/Containers/Data/Application/C5176D20-0015-4DFB-9156-D53DB6544F3E/tmp/SwiftR/SwiftR.js\'"></script>"
</body>
</html>
可以看到引用了三个js文件,其中上面的库是本地打包的;根据版本进行选择
打包的js中 SwiftR.js ,调用signalR库,同时和本地通信
window.swiftR = {
connection: null,
hubs: {},
transport: 'auto',
headers: {},
messages: {}
};
$(function() {
$.ajaxSetup({
beforeSend: function (jqxhr) {
for (var h in swiftR.headers) {
jqxhr.setRequestHeader(h, swiftR.headers[h]);
}
}
});
postMessage({ message: 'ready' });
});
function initialize(baseUrl, isHub) {
swiftR.connection = isHub ? $.hubConnection(baseUrl) : $.connection(baseUrl);
var connection = swiftR.connection;
connection.logging = true;
if (!isHub) {
connection.received(function(data) {
postMessage({ data: data });
});
}
connection.starting(function() {
postMessage({ message: 'starting' });
});
connection.connectionSlow(function() {
postMessage({ message: 'connectionSlow' });
});
connection.reconnecting(function() {
postMessage({ message: 'reconnecting' });
});
connection.reconnected(function() {
postMessage({ message: 'reconnected' });
});
connection.disconnected(function () {
postMessage({ message: 'disconnected' });
});
connection.error(function(error) {
postMessage({ message: 'error', error: processError(error) });
});
}
function start() {
swiftR.connection.start({ transport: swiftR.transport }).done(function() {
postMessage({ message: 'connected', connectionId: swiftR.connection.id });
}).fail(function() {
postMessage({ message: 'connectionFailed' });
});
}
function addHandler(id, hubName, method) {
var hub = ensureHub(hubName);
hub.on(method, function() {
postMessage({
id: id,
hub: hub.hubName,
method: method,
arguments: [].slice.call(arguments)
});
});
}
function postMessage(msg) {
var id = Math.random().toString(36).slice(2, 10);
swiftR.messages[id] = msg;
if (window.webkit) {
webkit.messageHandlers.interOp.postMessage(id);
} else {
var frame = $('<iframe/>', { src: 'swiftr://' + id });
$('body').append(frame);
frame.remove();
}
}
function ensureHub(name) {
var hub = swiftR.hubs[name];
if (!hub) {
hub = swiftR.connection.createHubProxy(name);
swiftR.hubs[name] = hub;
}
return hub;
}
function processError(error) {
var err = {
message: error.message || 'An unknown error has occurred.'
}
if (typeof error.source === 'string') {
err.source = error.source;
}
return err;
}
function readMessage(id) {
var msg = swiftR.messages[id];
delete swiftR.messages[id];
return window.webkit ? msg : JSON.stringify(msg);
}
其中下面的函数,可以通过webview的接口直接调用。
function start() {
swiftR.connection.start({ transport: swiftR.transport }).done(function() {
postMessage({ message: 'connected', connectionId: swiftR.connection.id });
}).fail(function() {
postMessage({ message: 'connectionFailed' });
});
}
function addHandler(id, hubName, method) {
var hub = ensureHub(hubName);
hub.on(method, function() {
postMessage({
id: id,
hub: hub.hubName,
method: method,
arguments: [].slice.call(arguments)
});
});
}
当有了回调信息之后,js可以通过下面的方法直接返回
function postMessage(msg) {
var id = Math.random().toString(36).slice(2, 10);
swiftR.messages[id] = msg;
if (window.webkit) {
webkit.messageHandlers.interOp.postMessage(id);
} else {
var frame = $('<iframe/>', { src: 'swiftr://' + id });
$('body').append(frame);
frame.remove();
}
}
在原生代码这边,通过下面的方法主动调用js
func runJavaScript(_ script: String, callback: ((Any?) -> ())? = nil) {
guard wkWebView != nil || webView != nil else {
jsQueue.append((script, callback))
return
}
if useWKWebView {
wkWebView.evaluateJavaScript(script, completionHandler: { (result, _) in
callback?(result)
})
} else {
let result = webView.stringByEvaluatingJavaScript(from: script)
callback?(result as AnyObject!)
}
}
js的消息通过下面的代码回调 WKScriptMessageHandler 机制
open func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if let id = message.body as? String {
wkWebView.evaluateJavaScript("readMessage('\(id)')", completionHandler: { [weak self] (msg, err) in
if let m = msg as? [String: Any] {
self?.processMessage(m)
} else if let e = err {
print("SwiftR unable to process message \(id): \(e)")
} else {
print("SwiftR unable to process message \(id)")
}
})
}
}
三、总结
实际使用验证了一下,看起来还比较稳定
这个工程给我的思路是,可以借鉴web上的解决方案为app服务,不需要局限在iOS开发的范围上。
四、附录
关于原生和js通信的方式:
https://www.jianshu.com/p/433e59c5a9eb