iOS使用SignalR客户端代码典范-桥接web SignalR 客户端库
一、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文件:
1 2 3 4 5 6 7 8 9 | < 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库,同时和本地通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 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的接口直接调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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可以通过下面的方法直接返回
1 2 3 4 5 6 7 8 9 10 11 12 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 机制
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架