P2PKit is a Bluetooth or WiFi Networking framework for hackathons! It is uses Apple's Multipeer Connectivity and works for all iOS, including Vision Pro, iPhone, and iPad.
- Offline data passing! For best performance across real devices, disable WiFi to use bluetooth connection.
- Automatic retrying when peers disconnect.
- Easy APIs for sending/receiving Codable data, with callbacks or with SwiftUI.
- Game Demo that uses P2PKit.
- APIs for setting/observing one device as host and the rest as followers.
- APIs for resetting session, changing the display name, and debugging.
You're the mallet with the star. When the white puck enters a hole, the last person to touch the white puck with their mallet scores a point!
P2PKitDemoPhone.mp4
3.Data.Passing.mov
P2PSynced is the easiest way to sync data across devices.
let syncedRoom = P2PSynced<GameRoom>(
name: "GameRoom",
initial: GameRoom(),
writeAccess: .hostOnly, // Optional param. Defaults to .everyone has write access. If using .hostOnly, set host with `P2PNetwork.makeMeHost()`.
reliable: true) // Optional param. Defaults to false. Reliable sending is slower but preserves order and doesn't drop messages.
// GET data
syncedRoom.onReceiveSync = { gameRoom in }
// SEND DATA
syncedRoom.value = GameRoom(...)
Use P2PSyncedObservable, a light wrapper around P2PSynced, with SwiftUI.
In this example, an Int value is sent between devices to sync a counter.
struct SyncedCounter: View {
@StateObject private var counter = P2PSyncedObservable(name: "SyncedCounter", initial: 1)
var body: some View {
Button("+ 1") {
counter.value = counter.value + 1 // SET VALUE
}
Text("Counter: \(counter.value)") // RECEIVE VALUE
}
}
Similarily, in the SyncedCircles example, the Codable object SendableCircle
, is synced across all devices.
@StateObject var blueCircle = P2PSyncedObservable(name: "blue", initial: SendableCircle(point: CGPoint(x: 300, y: -26)))
@StateObject var greenCircle = P2PSyncedObservable(name: "green", initial: SendableCircle(point: CGPoint(x: 260, y: -10)))
// RECEIVE
let sendableCircle = blueCircle.value
// SEND
blueCircle.value = SendableCircle(point: newPoint)
let malletDraggedEvents = P2PEventService<MalletDragEvent>("MalletDrag")
// RECEIVE
malletDraggedEvents.onReceive { eventInfo, malletDragEvent, json, sender in
// Handle malletDragEvent
}
// SEND
malletDraggedEvents.send(payload: MalletDragEvent(...), reliable: false)
Get myself and connected peers.
let myPeer: Peer = P2PNework.myPeer
let connectedPeers: [Peer] = P2PNework.connectedPeers
Observe peer updates with P2PNetworkPeerDelegate
. When connectedPeers update, the p2pNetwork(didUpdate peer: Peer)
handler will be called.
protocol P2PNetworkPeerDelegate: AnyObject {
func p2pNetwork(didUpdate peer: Peer)
func p2pNetwork(didUpdateHost host: Peer?)
}
P2PNework.addPeerDelegate(self)
P2PNework.removePeerDelegate(self)
(Optional) Reset the session.
P2PNework.resetSession("New Display Name") // Change display name.
P2PNework.resetSession(nil) // Keep current display name.
(Optional) Get and set the host for all connected devices. Not all games need a host.
let currentHost: Peer? = P2PNework.host
P2PNework.makeMeHost()
- Whoever taps "Create Room" acts as the host server with the game's source of truth, streaming game physics to everyone.
- Non-hosts disconnect and reconnects automatically, and their score will resume.
- When a host disconnects, other players get the "Continue Room" button, and the first player to tap that button becomes the host.
- Any previous host will accept the new host.
2.resume.game.mp4
When the current device connects to a host, P2PSynced
and P2PSyncedObservable
will sync the latest data from the host.