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

    

posted @   兜兜有糖的博客  阅读(2067)  评论(1编辑  收藏  举报
编辑推荐:
· 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 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示