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.
letsyncedRoom=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.
structSyncedCounter:View{@StateObjectprivatevarcounter=P2PSyncedObservable(name:"SyncedCounter", initial:1)varbody:someView{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.
@StateObjectvarblueCircle=P2PSyncedObservable(name:"blue", initial:SendableCircle(point:CGPoint(x:300, y:-26)))@StateObjectvargreenCircle=P2PSyncedObservable(name:"green", initial:SendableCircle(point:CGPoint(x:260, y:-10)))
// RECEIVE
letsendableCircle= blueCircle.value
// SEND
blueCircle.value =SendableCircle(point: newPoint)letmalletDraggedEvents=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.
letmyPeer:Peer=P2PNework.myPeerletconnectedPeers:[Peer]=P2PNework.connectedPeersObserve peer updates with P2PNetworkPeerDelegate. When connectedPeers update, the p2pNetwork(didUpdate peer: Peer) handler will be called.
protocolP2PNetworkPeerDelegate: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.
letcurrentHost:Peer?=P2PNework.hostP2PNework.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.