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文件:
<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
iOS使用SignalR客户端代码典范-桥接web SignalR 客户端库的更多相关文章
- ABP module-zero +AdminLTE+Bootstrap Table+jQuery权限管理系统第十六节--SignalR与ABP框架Abp.Web.SignalR及扩展
		
SignalR简介 SignalR是什么? ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程.实时 Web 功能是指 ...
 - SignalR 实现web浏览器客户端与服务端的推送功能
		
SignalR 是一个集成的客户端与服务器库,基于浏览器的客户端和基于 ASP.NET 的服务器组件可以借助它来进行双向多步对话. 换句话说,该对话可不受限制地进行单个无状态请求/响应数据交换:它将继 ...
 - 【SignalR学习系列】7. SignalR Hubs Api 详解(JavaScript 客户端)
		
SignalR 的 generated proxy 服务端 public class ContosoChatHub : Hub { public void NewContosoChatMessage( ...
 - 使用ASP.NET MVC Web SignalR 构建单身聊天室(一)
		
前言:本系列的头章,想要带大家一起学习Web SignalR,那它是什么呢?ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么 ...
 - 比官方教程代码更简短的SignalR Server Broadcast示例
		
SignalR是微软ASP.NET技术体系中的新成员. 在www.asp.net网站上的SignalR专区有一篇SignalR的入门级教程<Tutorial: Server Broadcast ...
 - 在C#开发中如何使用Client Object Model客户端代码获得SharePoint 网站、列表的权限情况
		
自从人类学会了使用火,烤制的方式替代了人类的消化系统部分功能,从此人类的消化系统更加简单,加速了人脑的进化:自从SharePoint 2010开始有了Client Side Object Model ...
 - iOS流行的开源代码库
		
本文介绍一些流行的iOS的开源代码库 1.AFNetworking 更新频率高的轻量级的第三方网络库,基于NSURL和NSOperation,支持iOS和OSX.https://github.com/ ...
 - 使用Myeclipse插件将wsdl生成java客户端代码
		
使用环境:MyEclipse9.0 本教程使用Myeclipse内置插件生成java代码,网上说这是xfire插件,不管怎样,生成和调用客户端代码都十分简单. 1.在项目上右键,选择New->O ...
 - 初学svn对版本进行控制 用post- commit钩子实现代码同步到web目录
		
这里只是一个记录,原文摘抄svn利用钩子实现代码同步到web目录 思路: 找 到SVN Server中的仓库(Repositories)文件夹的位置,在相应的项目文件夹中找到hooks文件夹.在该文件 ...
 - webservice 服务端例子+客户端例子+CXF整合spring服务端测试+生成wsdl文件 +cxf客户端代码自动生成
		
首先到CXF官网及spring官网下载相关jar架包,这个不多说.webservice是干嘛用的也不多说. 入门例子 模拟新增一个用户,并返回新增结果,成功还是失败. 大概的目录如上,很简单. Res ...
 
随机推荐
- vue-cli4.0 (vue3.0 的脚手架)
			
前言: 这个搭建脚手架的话实际是我们创建一个新项目的第一步,当然,现在脚手架4.0都出来了,经过使用后发现跟我们之前的3.0使用方法是答题一样的,其中用vue-cli3.0来搭建我们的项目的话又分为两 ...
 - 传统 Web 框架部署与迁移
			
简介: 与其说 Serverless 架构是一个新的概念,不如说它是一种全新的思路,一种新的编程范式. 与其说 Serverless 架构是一个新的概念,不如说它是一种全新的思路,一种新的编程范式. ...
 - 做ToB软件质量保障的这两年
			
简介:自己算是阿里的老兵了,从实习开始一直投身在 toB 业务的质量保障领域内,不能说是资深的专家,但所经历的.感受的业务特点和体会还是具有一定的代表性,希望能通过这篇文章,总结一下过往,并能和已经 ...
 - [Trading] 如何应对股价下跌, 投资技巧
			
如果你想利用股价下跌的机会,你可以做以下事情来潜在地赚取利润. 为什么股价下跌会吓到投资者 商品在低价时,我们都倾向于买入,对于股票,价格暴跌以投资的形式损失你的钱,令人产生卖出情绪. 然而在卖掉之前 ...
 - C语言程序设计-笔记2-分支结构
			
C语言程序设计-笔记2-分支结构 例3-1 简单的猜数游戏.输入你所猜的整数(假定1-100),与计算机产生的被猜数比较,若相等,显示猜中:若不等,显示与被猜数的大小关系. /*简单的猜数游戏*/ ...
 - js部分数组方法
			
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
 - vue.js+canvas实现随机验证码
			
登录注册啥的,不需要下载插件,上图: 代码: <template> <div class="about"> <p>当前验证码:{{codeStr ...
 - Selenium使用总结:加载Flash、禁用JS、滚动页面至元素、缩放页面
			
前言 前几周做了个使用Selenium的项目,踩了好多好多好多的Selenium的坑,越来越感觉他作为一个第三方库,对于Chrome的操作实在是有局限.另外,推荐大家一个Selenium之外的操作浏览 ...
 - JUC并发编程学习笔记(十九)原子引用
			
原子引用 带版本号的原子操作! 解决ABA问题,引入原子引用(乐观锁思想) AtomicStampedReference类解决ABA问题 package org.example.cas; import ...
 - 05.Java 方法详解
			
1.方法的定义及调用 设计方法的原则:一个方法只完成一个功能,有利于后期的扩展 方法的定义: 修饰符(可选) 返回值类型 方法名(参数类型 参数名(可选)){ 方法体 return 返回值; } 2. ...