RxMultipeer alternatives and similar libraries
Based on the "Reactive Programming" category.
Alternatively, view RxMultipeer alternatives based on common mentions on social networks and blogs.
-
ReactiveCocoa
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift. -
OpenCombine
Open source implementation of Apple's Combine framework for processing values over time. -
Katana
Swift Apps in a Swoosh! A modern framework for creating iOS apps, inspired by Redux. -
RxCoordinator
๐ Powerful navigation library for iOS based on the coordinator pattern -
RxAlamofire
RxSwift wrapper around the elegant HTTP networking in Swift Alamofire -
Interstellar
Simple and lightweight Functional Reactive Coding in Swift for the rest of us. :large_orange_diamond: -
RxAutomaton
๐ค RxSwift + State Machine, inspired by Redux and Elm. -
NSObject-Rx
Handy RxSwift extensions on NSObject, including rx.disposeBag. -
Verge
๐ฃ A robust Swift state-management framework designed for complex applications, featuring an integrated ORM for efficient data handling. -
RxMediaPicker
A reactive wrapper built around UIImagePickerController. -
VueFlux
:recycle: Unidirectional State Management Architecture for Swift - Inspired by Vuex and Flux -
Komponents ๐ฆ
๐ฆ React-inspired UIKit Components - โ ๏ธ Deprecated -
ReactiveTask
Flexible, stream-based abstraction for launching processes -
TemplateKit
React-inspired framework for building component-based user interfaces in Swift. -
RxReduce
Lightweight framework that ease the implementation of a state container pattern in a Reactive Programming compliant way. -
LightweightObservable
๐ฌ A lightweight implementation of an observable sequence that you can subscribe to. -
Aftermath
:crystal_ball: Stateless message-driven micro-framework in Swift. -
ReactiveArray
An array class implemented in Swift that can be observed using ReactiveCocoa's Signals -
SimpleApiClient
A configurable api client based on Alamofire4 and RxSwift4 for iOS. -
OneWay
A Swift library for state management with unidirectional data flow. -
ACKReactiveExtensions
Set of useful extensions for ReactiveSwift & ReactiveCocoa -
ReactiveLocation
ReactiveCocoa wrapper for CLLocationManager. -
BindKit
Two-way data binding framework for iOS. Only one API to learn. -
STDevRxExt
STDevRxExt contains some extension functions for RxSwift and RxCocoa which makes our live easy. -
RxOptional
RxSwift extentions for Swift optionals and "Occupiable" types -
RxAlamoRecord
RxAlamoRecord combines the power of the AlamoRecord and RxSwift libraries to create a networking layer that makes interacting with API's easier than ever reactively.
Appwrite - The Open Source Firebase alternative introduces iOS support
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of RxMultipeer or a related project?
README
A testable RxSwift wrapper around MultipeerConnectivity
RxMultipeer is a RxSwift wrapper for MultipeerConnectivity.
Using the adapter pattern, we can test multipeer code with heavy mocking. In effect, we are trying to isolate all the
untestable bits of MultipeerConnectivity
into one library.
This library also gives you the flexibility to swap out the underlying mechanics of p2p with some other protocol such as websockets. At the moment it only comes with support for Apple's MultipeerConnectivity, however you can easily write your own adapters for different protocols.
Installation
Carthage
Add this to your Cartfile
github "RxSwiftCommunity/RxMultipeer" ~> 3.0
Example code
For a working example check out the RxMultipeer Example
folder.
Advertise and accept nearby peers
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)
Browse for and connect to peers
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)
Sending and receiving strings
Sending them:
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)
And receiving them:
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)
Establishing a data stream
RxSwift makes sending streaming data to a persistent connection with another peer very intuitive.
The sender:
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)
The receiver:
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)
Usage
Imports
import RxSwift
import RxMultipeer
Make a new build configuration for testing
Your project comes with Debug
and Release
build configurations by default, we need to make a new one called
Testing
. Please check here for step-by-step instructions.
Setting up the client
// 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
Supported transfer resource types
String
func send(toPeer: Client, string: String, mode: MCSessionSendDataMode = .Reliable) -> Observable<()>
func receive() -> Observable<(Client, String)>
Data
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]>
Testing
When testing, use preprocesser macros to ensure that your code uses a MockSession
instance instead of
MultipeerConnectivitySession
one. In order to achieve this you need to use preprocessor flags and swap out anywhere
that references Client<T>
(because T
will be different depending on whether you are testing or not.) First you will
need to set up a new build configuration, and then you can use preprocessor macros like so:
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
Don't worry, you should only really need preprocessor macros in one centralized place, the type of your client can be inferred by the compiler thereafter.
Mocking other nearby peers in the test environment then becomes as simple as creating other CurrentClient(session:
MockSession(name: "other"))
. For example, if your app is running in a testing environment the following code will mock
a nearby client:
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)
Breaking changes
Version 3.0.0
Starting from version 3.0.0
, incomingCertificateVerifications() -> Observable<(MCPeerID, [Any]?, (Bool) -> Void)>
was introduced. This needs to be implemented in order for mock and real
connections to work. Behaviour prior to this update can be reproduced by simply
accepting all certificates:
let client: CurrentClient<MCPeerID
client.incomingCertificateVerifications()
.subscribe(onNext: { (peer, certificateChain, respond) in
// Validate the certificateChain
respond(true)
})
.addDisposableTo(disposeBag)
Contributing
- Indent with 2 spaces
- Strip trailing whitespace
- Write tests
- Pull-request from feature branches.