测试已测试 | ✓ |
Lang语言 | SwiftSwift |
许可证 | MIT |
Released最新发布 | 2017年1月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✗ |
由 Nathan Kot 维护。
依赖项 | |
RxSwift | ~> 3.0 |
RxCocoa | ~> 3.0 |
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
您的项目默认带有 Debug
和 Release
构建配置,我们需要创建一个新的,命名为 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)>
func send(toPeer: Client, json: [String: Any], mode: MCSessionSendDataMode = .Reliable) -> Observable<()>
func receive() -> Observable<(Client, [String: Any])>
func send(toPeer: Client, name: String, url: NSURL, mode: MCSessionSendDataMode = .Reliable) -> Observable<NSProgress>
func receive() -> Observable<(Client, String, ResourceState)>
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
开始,引入了 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)