RxMultipeer 3.0.1

RxMultipeer 3.0.1

测试已测试
Lang语言 SwiftSwift
许可证 MIT
Released最新发布2017年1月
SwiftSwift 版本3.0
SPM支持 SPM

Nathan Kot 维护。



 
依赖项
RxSwift~> 3.0
RxCocoa~> 3.0
 

  • 作者
  • Nathan Kot

A testable RxSwift wrapper around MultipeerConnectivity

RxMultipeer 是一个针对 MultipeerConnectivity 的 RxSwift 包装器。

使用适配器模式,我们可以通过重做模拟来测试多对多代码。实质上,我们将所有不可测试的 MultipeerConnectivity 部分隔离到一个库中。

这个库还为您提供了灵活性,您可以用一些其他协议(如websocket)替换底层的 P2P 机制。目前它只支持 Apple 的 MultipeerConnectivity,您也可以轻松编写适配不同协议的自定义适配器。

示例代码

为了一个工作示例,请查看 RxMultipeer Example 文件夹。

广告并接受附近的对等方

import RxSwift
import RxCocoa
import RxMultipeer

let disposeBag: DisposeBag
let acceptButton: UIButton
let client: CurrentClient<MCPeerID>

client.startAdvertising()
let connectionRequests = client.incomingConnections().shareReplay(1)

acceptButton.rx_tap
  .withLatestFrom(connectionRequests)
  .subscribe(onNext: { (peer, context, respond) in respond(true) })
  .addDisposableTo(disposeBag)

client.incomingCertificateVerifications()
    .subscribe(onNext: { (peer, certificateChain, respond) in
      // Validate the certificateChain
      respond(true)
    })
    .addDisposableTo(disposeBag)

浏览和连接到对等方

import RxSwift
import RxMultipeer

let disposeBag: DisposeBag
let client: CurrentClient<MCPeerID>

client.startBrowsing()

let nearbyPeers = client.nearbyPeers().shareReplay(1)

// Attempt to connect to all peers
nearbyPeers
  .map { (peers: [Client<MCPeerID>]) in
    peers.map { client.connect(toPeer: $0, context: ["Hello": "there"], timeout: 12) }.zip()
  }
  .subscribe()
  .addDisposableTo(disposeBag)

发送和接收字符串

发送它们

import RxSwift
import RxCocoa
import RxMultipeer

let disposeBag: DisposeBag
let client: CurrentClient<MCPeerID>
let peer: Observable<Client<MCPeerID>>
let sendButton: UIButton

sendButton.rx_tap
  .withLatestFrom(peer)
  .map { client.send(toPeer: peer, string: "Hello!") }
  .switchLatest()
  .subscribe(onNext: { _ in print("Message sent") })
  .addDisposableTo(disposeBag)

并接收它们

import RxSwift
import RxMultipeer

let disposeBag: DisposeBag
let client: CurrentClient<MCPeerID>

client.receive()
.subscribe(onNext: { (peer: Client<MCPeerID>, message: String) in
  print("got message \(message), from peer \(peer)")
})
.addDisposableTo(disposeBag)

建立数据流

RxSwift 使向另一个对等方的持久连接发送流数据变得非常直观。

发送方

import RxSwift
import RxMultipeer

let disposeBag: DisposeBag
let client: CurrentClient<MCPeerID>
let peer: Observable<Client<MCPeerID>>
let queuedMessages: Observable<[UInt8]>

let pipe = peer.map { client.send(toPeer: peer, streamName: "data.stream") }
pipe.withLatestFrom(queuedMessages) { $0 }
  .subscribe(onNext: { (sender, message) in sender(message) })
  .addDisposableTo(disposeBag)

接收方

import RxSwift
import RxMultipeer

let disposeBag: DisposeBag
let client: CurrentClient<MCPeerID>
let peer: Observable<Client<MCPeerID>>

let incomingData = client.receive(fromPeer: peer, streamName: "data.stream").shareReplay(1)
incomingData
  .subscribe(onNext: { (data) in print(data) })
  .addDisposableTo(disposeBag)

用法

导入

import RxSwift
import RxMultipeer

为测试创建一个新的构建配置

您的项目默认带有 DebugRelease 构建配置,我们需要创建一个新的,命名为 Testing。请在此处查看逐个步骤说明

设置客户端

// See the link above,
// You'll need to define a new build configuration and give it the `TESTING` flag
let name = UIDevice.currentDevice().name
#if TESTING
typealias I = MockIden
let client = CurrentClient(session: MockSession(name: name))
#else
typealias I = MCPeerID
let client = CurrentClient(session: MultipeerConnectivitySession(
                 displayName: name,
                 serviceType: "multipeerex",
                 idenCacheKey: "com.rxmultipeer.example.mcpeerid",
                 encryptionPreference: .None))
#endif

支持传输资源类型

字符串

func send(toPeer: Client, string: String, mode: MCSessionSendDataMode = .Reliable) -> Observable<()>
func receive() -> Observable<(Client, String)>

数据

func send(toPeer: Client, data: Data, mode: MCSessionSendDataMode = .Reliable) -> Observable<()>
func receive() -> Observable<(Client, Data)>

JSON

func send(toPeer: Client, json: [String: Any], mode: MCSessionSendDataMode = .Reliable) -> Observable<()>
func receive() -> Observable<(Client, [String: Any])>

NSURL

func send(toPeer: Client, name: String, url: NSURL, mode: MCSessionSendDataMode = .Reliable) -> Observable<NSProgress>
func receive() -> Observable<(Client, String, ResourceState)>

NSStream

func send(toPeer: Client, streamName: String, runLoop: NSRunLoop = NSRunLoop.mainRunLoop()) -> Observable<([UInt8]) -> Void>
func receive(fromPeer: Client, streamName: String, runLoop: NSRunLoop = NSRunLoop.mainRunLoop(), maxLength: Int = 512) -> Observable<[UInt8]>

测试

在测试时,使用预处理宏以确保您的代码使用 MockSession 实例而不是 MultipeerConnectivitySession。为了实现这一点,您需要使用预处理标志,并将任何引用 Client<T> 的地方替换掉(因为 T 将根据您是否在测试中而有所不同。)首先您需要设置一个新的构建配置,然后您可以使用预处理宏,如下所示:

let name = UIDevice.currentDevice().name
#if TESTING
typealias I = MockIden
let client = CurrentClient(session: MockSession(name: name))
#else
typealias I = MCPeerID
let client = CurrentClient(session: MultipeerConnectivitySession(
                 displayName: name,
                 serviceType: "multipeerex",
                 idenCacheKey: "com.rxmultipeer.example.mcpeerid",
                 encryptionPreference: .None))
#endif

别担心,您可能只需要在单个集中位置使用预处理宏,编译器还将据此推断客户端的类型。

模拟测试环境中的其他附近节点变得非常简单,只需创建其他 CurrentClient(session: MockSession(name: "other"))。例如,如果您的应用正在测试环境中运行,以下代码将模拟一个附近的客户端

let disposeBag: DisposeBag
let otherclient = CurrentClient(session: MockSession(name: "mockedother"))

// Accept all connections
otherclient.startAdvertising()

otherclient.incomingConnections()
  .subscribeNext { (client, context, respond) in respond(true) }
  .addDisposableTo(disposeBag)

// Starting from version 3.0.0 the following handler needs to be implemented
// for this library to work:
otherclient.incomingCertificateVerifications()
  .subscribeNext { (client, certificateChain, respond) in respond(true) }
  .addDisposableTo(disposeBag)

// Respond to all messages with 'Roger'
otherclient.receive()
  .map { (client: Client<MockIden>, string: String) in otherclient.send(toPeer: client, "Roger") }
  .concat()
  .subscribeNext { _ in print("Response sent") }
  .addDisposableTo(disposeBag)

破坏性更改

版本 3.0.0

从版本 3.0.0 开始,引入了 incomingCertificateVerifications() -> Observable<(MCPeerID, [Any]?, (Bool) -> Void)>。为了使模拟和真实连接工作,需要实现该功能。更新之前的行为可以通过简单地接受所有证书来重现。

let client: CurrentClient<MCPeerID
client.incomingCertificateVerifications()
    .subscribe(onNext: { (peer, certificateChain, respond) in
      // Validate the certificateChain
      respond(true)
    })
    .addDisposableTo(disposeBag)

贡献

  • 使用两个空格缩进
  • 删除尾随空格
  • 编写测试
  • 从功能分支中拉取请求。