Skip to content

About

Functional wrapper for Apple's MultipeerConnectivity framework.

Resources

Stars

56 stars

Watchers

2 watching

Forks

Latest commit

ย 

History

365 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

PeerConnectivity

CI Platform: iOS 13+ / macOS 10.15+ Language: Swift 5 SwiftPM compatible License: MIT

A functional wrapper for the MultipeerConnectivity framework.

PeerConnectivity is meant to have a lightweight easy to use syntax, be extensible and flexible, and handle the heavy lifting and edge cases of the MultipeerConnectivity framework quietly in the background.

Please open an issue or submit a pull request if you have any suggestions!

๐Ÿ”Œ Blog post https://goo.gl/HJcMbE

Installation

Swift Package Manager

Add PeerConnectivity as a Swift Package dependency:

.package(url: "https://github.com/rchatham/PeerConnectivity.git", from: "0.6.0")

Use the core networking product for UIKit-free peer connectivity:

.product(name: "PeerConnectivity", package: "PeerConnectivity")

Add the UI helper product only when you need UIKit browser view controller support:

.product(name: "PeerConnectivityUI", package: "PeerConnectivity")

CocoaPods and Carthage are no longer the recommended distribution paths for new releases.

Migration documentation

Experimental Network framework backend

PeerConnectionManager can be explicitly initialized with backend: .networkFramework on supported OS versions. The default backend remains .multipeerConnectivity.

Use .preSharedKey with at least 32 CSPRNG-generated bytes of app-managed key material for encrypted Network sessions authenticated as members of the same key-sharing group:

let secret : Data = loadProvisionedNetworkPSK() // At least 32 random bytes; app-defined provisioning.
let pcm = PeerConnectionManager(serviceType: "local",
    backend: .networkFramework,
    networkSecurity: .preSharedKey(secret))

The default networkSecurity: .unauthenticated mode is plaintext TCP, remains available only for source compatibility and diagnostics, and must not be used for sensitive data.

Network adopters must add local-network privacy metadata to the app target, not the package or framework plist. For the service type above, the minimum declarations are:

<key>NSLocalNetworkUsageDescription</key>
<string>Discover and connect to nearby devices running this app.</string>
<key>NSBonjourServices</key>
<array>
    <string>_local._tcp</string>
</array>

The backend maps the bare serviceType: "local" to the TCP Bonjour type _local._tcp; declare every service type the app uses. It does not advertise _local._udp. Physical-device testing is required to validate the Local Network permission flow and the app's supported Wi-Fi/peer-to-peer topologies; simulator success is not sufficient for production readiness.

See NetworkBackendGuide.md for the complete production setup, service-type mapping, physical-device checklist, support matrix, demo launch arguments, and known limitations. See NetworkTrustModelPlan.md for the current group-membership boundary, display-name spoofing threat model, and future individual-identity options.

Stream and resource APIs

The current Network backend supports reliable Data and PeerMessage exchange, but it does not implement a custom stream or resource-transfer protocol. The following APIs remain available and supported only when the manager uses .multipeerConnectivity:

  • sendDataStream(streamName:toPeer:)
  • sendResourceAtURL(_:withName:toPeers:withCompletionHandler:)
  • .receivedStream
  • .startedReceivingResource and .finishedReceivingResource

With .networkFramework, stream sends throw an unsupported-operation error, resource sends return nil progress and report an unsupported-operation error, and the corresponding receive events are never emitted. Use sendData or sendMessage for bounded payloads, or retain the default MultipeerConnectivity backend when stream/resource behavior is required. See the compatibility decision for rationale and exact behavior.

Creating/Stopping/Starting

var pcm = PeerConnectionManager(serviceType: "local")

// Start
pcm.start()

// Stop
//  - You should always stop the connection manager 
//    before attempting to create a new one
pcm.stop()

// Can join chatrooms using PeerConnectionType.automatic, .inviteOnly, and .custom
//  - .automatic : automatically searches and joins other devices with the same service type
//  - .inviteOnly : provides MultipeerConnectivity advertiser assistant behavior; Network backend requires app-owned UI using found/lost peer events
//  - .custom : no default behavior is implemented

// The manager can be initialized with a contructed peer representing the local user
// with a custom displayName

pcm = PeerConnectionManager(serviceType: "local", connectionType: .automatic, displayName: "I_AM_KING")

// Start again at any time
pcm.start() {
    // Do something when finished starting the session
}

Browser UI

UIKit browser view controller support lives in the separate PeerConnectivityUI SwiftPM product and is available only for the MultipeerConnectivity backend:

import PeerConnectivity
import PeerConnectivityUI

let pcm = PeerConnectionManager(serviceType: "local", connectionType: .inviteOnly)
let browserViewController = pcm.browserViewController { event in
    switch event {
    case .didFinish, .wasCancelled, .none:
        break
    }
}

Use the optional peerFilter to hide nearby peers before they are presented by MCBrowserViewController. Discovery info is public, unauthenticated metadata, so only use it for non-secret values such as protocol versions, public capabilities, or non-secret room labels.

let filteredBrowserViewController = pcm.browserViewController({ _ in }, peerFilter: { peer, discoveryInfo in
    return discoveryInfo?["protocol"] == "2"
})

The Network backend does not have an MCBrowserViewController equivalent. During this migration phase, PeerBrowserModel is the supported foundation for app-owned UIKit or SwiftUI peer-selection UI:

let browserModel = PeerBrowserModel(manager: pcm) { peers in
    // Update app-owned UI; this callback runs on the main queue.
}

browserModel.startObserving()

if let approvedPeer = browserModel.discoveredPeers.first {
    browserModel.invitePeer(approvedPeer)
}

A reusable Network browser view is intentionally deferred until app-owned integrations establish common UI requirements. See NetworkBackendGuide.md for the decision and lifecycle guidance.

Demo App

Run PeerConnectivityDemo.xcodeproj on two simulators or devices. Before starting, select the backend (Multipeer or Network), connection behavior (Automatic or Require Invitation), and backend-specific security. Multipeer offers compatible optional encryption or required encryption; because the demo has a nil identity and accepts all certificates, neither setting authenticates peers and both remain MITM-vulnerable. Network offers unauthenticated plain TCP or TLS with a strictly validated 32+-byte Base64 shared key. TLS-PSK authenticates group membership, not individual identity, and invalid input blocks Start without downgrading. The demo remembers each backend's selection while running; security and connection behavior remain independent and disabled while networking runs. See PeerConnectivityDemo/README.md for key handling, debug-only launch arguments, and the full validation flow. Both peers must select the same backend; no interoperability bridge is included.

API Compatibility Notes

Network backend discovery metadata limitation

The opt-in .networkFramework backend does not currently support app-provided discoveryInfo. A value passed to PeerConnectionManager is retained as configuration but ignored by Network advertising; the backend's Bonjour TXT record contains only reserved internal peer identity fields. Peers discovered through the Network backend therefore emit foundPeerWithDiscoveryInfo(peer:discoveryInfo:) with discoveryInfo == nil. Use the default .multipeerConnectivity backend when application discovery metadata is required.

PeerConnectionEvent.foundPeer(peer:) is still emitted for existing listeners. When advertised discovery metadata is available, PeerConnectivity also emits foundPeerWithDiscoveryInfo(peer:discoveryInfo:); listeners should handle one of these discovery events to avoid processing the same peer twice. Callers with exhaustive switches over PeerConnectionEvent need to add the new case or a default branch.

In .automatic mode, invitation decisions are controlled by PeerInvitationPolicy. Non-manual automatic policies still emit .receivedInvitation for observation/API compatibility, but that event's handler is a no-op and does not change the policy decision.

Sending Events to Peers

let event: [String: Any] = [
    "EventKey" : Date()
]

// Sends to all connected peers
pcm.sendEvent(event)


// Use this to access the connectedPeers
let connectedPeers: [Peer] = pcm.connectedPeers

// Events can be sent to specific peers
if let somePeerThatIAmConnectedTo = connectedPeers.first {
   pcm.sendEvent(event, toPeers: [somePeerThatIAmConnectedTo])
}

Detecting send failures

sendData and sendMessage also have completion overloads that report per-peer failures on the main queue. An empty result means the local networking stack accepted every send; it does not confirm delivery. See Send outcomes.

pcm.sendData(payload, toPeers: pcm.connectedPeers) { failures in
    failures.forEach { peer, error in print("\(peer.displayName): \(error)") }
}

Listening for Events

// Listen to an event
pcm.observeEventListenerForKey("someEvent") { (eventInfo, peer) in
    
    print("Received some event \(eventInfo) from \(peer.displayName)")
    guard let date = eventInfo["eventKey"] as? Date else { return }
    print(date)
    
}

// Stop listening to an event
pcm.removeListenerForKey("SomeEvent")

Acknowledgments

Icon from the Noun Project.

Helpful links

About

Functional wrapper for Apple's MultipeerConnectivity framework.

Resources

Stars

56 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages