Get Started with p2pkit for iOS / macOS
Enable your apps to understand their proximity to nearby devices.
API Overview
p2pkit has two very simple to understand APIs:
Lifecycle API | Manages the lifecycle and account validation of p2pkit |
---|---|
Discovery API | Manages all aspects of the discovery functionality |
Download
Download p2pkit.framework (2.1.0):
Get your Application Key
In case you do not yet have an application key you would need to obtain one in the p2pkit Console
Create a new app
Source code for the p2pkit Quickstart app is available on GitHub:
https://github.com/Uepaa-AG/p2pkit-quickstart-ios
Setup
P2PKit.framework supports both Objective-C and Swift. The SDK is built with ARC (automatic reference counting) and supports iOS 7.1+ and macOS 10.10+.
-
Add dependency
There are two ways how you can add p2pkit to your project. Either through CocoaPods or manually.
CocoaPods
If you are using CocoaPods, then add these lines to your Podfile:
platform :ios, '7.1' pod 'p2pkit'
Or for macOS:
platform :osx, '10.10' pod 'p2pkit'
Manual setup (without CocoaPods)
- Add p2pkit
- Drag P2PKit.framework into your Xcode project folder. (Make sure the "Copy items if needed" is checked)
- Add dependencies
- Click on Targets > Your App > and then the 'Build Phases' tab
- Expand 'Link Binary With Libraries' and make sure P2PKit.framework is in the list, if not then add it!
- Click the + button and add the additional dependencies mentioned below:
- CoreBluetooth.framework
- libicucore.tbd
- CFNetwork.framework
- Security.framework
- Foundation.framework
- Add p2pkit
-
Add permissions
For the discovery to work, you need to add the necessary BLE permission and usage description to your
Info.plist
file.<key>UIBackgroundModes</key> <array> <string>bluetooth-central</string> <string>bluetooth-peripheral</string> </array> <key>NSBluetoothPeripheralUsageDescription</key> <string>So you can discover and be discovered by other peers nearby.</string>
-
Set Bundle ID
If not yet done, head back to the p2pkit console and associate the correct Bundle ID with your application key. p2pkit will validate the Bundle ID when launched.
Bitcode is currently not supported by p2pkit. To disable Bitcode in your project: Build Settings > Build Options > Enable Bitcode: No.
Import
Import the p2pkit header.
#import <P2PKit/P2PKit.h>
In your project Objective-C Bridging Header file #import <P2PKit/P2PKit.h>
If your project does not have an "Objective-C Bridging Header" file, please refer to the Swift documentation for instructions on how to create one.
Lifecycle - Initialization
Initialize p2pkit with your personal application key.
[PPKController enableWithConfiguration:@"<YOUR APPLICATION KEY>" observer:self];
PPKController.enableWithConfiguration("<YOUR APPLICATION KEY>", observer:self)
Lifecycle - Delegate
Conform to the PPKControllerDelegate
protocol by declaring it in your class and implementing the corresponding methods
Declare:
@interface MyClass : NSObject <PPKControllerDelegate> { } @end
Class MyClass : NSObject, PPKControllerDelegate { }
Implement:
-(void)PPKControllerInitialized { // ready to start discovering nearbys }
func PPKControllerInitialized() { // ready to start discovering nearbys }
Discovery
With p2p discovery you can understand your proximity to nearby devices. p2pkit supports discovery, lost, payload and ranging APIs.
Start p2p discovery and pass in additional discovery information (max 440 bytes) which will be received by other peers nearby.
[PPKController startDiscoveryWithDiscoveryInfo:[@"Hello" dataUsingEncoding:NSUTF8StringEncoding] stateRestoration:NO];
PPKController.startDiscovery(withDiscoveryInfo: "Hello".data(using: .utf8), stateRestoration: false)
Discovery info is transferred over our cloud, however, it is not end-to-end encrypted. Hence we recommend not sending any sensitive data through this means.
Implement the PPKControllerDelegate
protocol to receive discovery events and their corresponding peers.
-(void)peerDiscovered:(nonnull PPKPeer*)peer { NSString *discoveryInfoString = [[NSString alloc] initWithData:peer.discoveryInfo encoding:NSUTF8StringEncoding]; NSLog(@"%@ is here with discovery info: %@", peer.peerID, discoveryInfoString); } -(void)peerLost:(nonnull PPKPeer*)peer { NSLog(@"%@ is no longer here", peer.peerID); }
func peerDiscovered(_ peer: PPKPeer) { if let discoveryInfo = peer.discoveryInfo { let discoveryInfoString = String(data: discoveryInfo, encoding: .utf8) print("\(peer.peerID) is here with discovery info: \(discoveryInfoString)") } } func peerLost(_ peer: PPKPeer) { print("\(peer.peerID) is no longer here") }
At a later stage, you can publish new discovery info which will be pushed to nearby peers.
Updates are throttled to one per 60 seconds and disseminated to nearby peers on a best-effort basis. If a nearby peer has lost his connection to our cloud he will not receive the updated version until he is rediscovered.
[PPKController pushNewDiscoveryInfo:[@"Hello again!" dataUsingEncoding: NSUTF8StringEncoding]];
PPKController.pushNewDiscoveryInfo("Hello again!".data(using: .utf8))
Receive an updated discovery info by implementing the corresponding delegate methods.
-(void)discoveryInfoUpdatedForPeer:(nonnull PPKPeer*)peer { NSString *discoveryInfo = [[NSString alloc] initWithData:peer.discoveryInfo encoding:NSUTF8StringEncoding]; NSLog(@"%@ has updated discovery info: %@", peer.peerID, discoveryInfo); }
func discoveryInfoUpdated(for peer: PPKPeer) { if let discoveryInfo = peer.discoveryInfo { let discoveryInfoString = String(data: discoveryInfo, encoding: .utf8) print("\(peer.peerID) has updated discovery info: \(discoveryInfoString)") } }
Important: Please make sure you stopDiscovery
when your end-user no longer wishes to discover or be discovered.
CoreBluetooth State Restoration on iOS
p2pkit can use the CoreBluetooth State Restoration API. State restoration enables p2pkit-enabled apps to continue to discover and be discovered even if the application has crashed or was terminated by the OS.
To enable state restoration, pass in YES/true when starting discovery
[PPKController startDiscoveryWithDiscoveryInfo:myDiscoveryInfo stateRestoration:YES];
PPKController.startDiscovery(withDiscoveryInfo:myDiscoveryInfo, stateRestoration: true)
Then, when the application is relaunched due to a state restoration event don't forget to enable p2pkit and start the discovery once more.
You can use the launchOptions
in the below UIApplicationDelegate
methods to understand if the app is being launched into the background due to a state restoration event.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
optional func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
Refer to UIApplicationDelegate Protocol Reference for more details
Important: Please make sure you stopDiscovery
when your end-user no longer wishes to discover or be discovered, especially when using the state restoration API
Proximity Ranging
Proximity Ranging adds context to the discovery events by providing 5 levels of proximity strength from “immediate” to “extremely weak” (see PPKProximityStrength
).
You could associate "proximity strength" with distance, but due to the unreliable nature of signal strength (different hardware, environmental conditions, etc.) we preferred not to associate the two. Nevertheless, in many cases you will be able to determine who is the closest peer to you (if he is significantly closer than others).
The first step is to enable Proximity Ranging.
[PPKController enableProximityRanging];
PPKController.enableProximityRanging()
Proximity Strength is a property of to the PPKPeer
object. Updates are delivered when the proximity strength of a nearby peer changes, to receive updates you would need to implement the following delegate method.
-(void)proximityStrengthChangedForPeer:(nonnull PPKPeer*)peer { if (peer.proximityStrength > PPKProximityStrengthWeak) { NSLog(@"%@ is in range, do something with it", peer.peerID); } else { NSLog(@"%@ is not yet in range", peer.peerID); } }
func proximityStrengthChanged(for peer: PPKPeer) { if (peer.proximityStrength.rawValue > PPKProximityStrength.weak.rawValue) { print("\(peer.peerID) is in range, do something with it") } else { print("\(peer.peerID) is not yet in range") } }
If the proximity strength for a peer cannot be determined, the proximity strength value will be PPKProximityStrengthUnknown
. Proximity ranging only works in the foreground. Not all Android devices have the capability to be ranged by other peers.
Messaging
Important: Messaging is only available for Pro Edition accounts.
Messaging can be used to interact with a nearby peer following a discovery event. Contrary to the broadcast nature of the discovery info, a message is always directed to a single recipient.
Sending messages
Once a peer is discovered, use the following API to send a message.
NSData *message = [@"hello" dataUsingEncoding:NSUTF8StringEncoding]; [PPKController sendMessage:message toNearbyPeer:peer withDeliveryStatusBlock:^(PPKMessageDeliveryCode statusCode) { if (statusCode == PPKMessageDeliveryCodeDispatched) { NSLog(@"Message sent to peer: %@", peer.peerID); } else { NSLog(@"Failed to send the message with error code: %ld", (long)statusCode); } }];
let message = "hello".data(using: .utf8) PPKController.sendMessage(message!, toNearbyPeer: peer, { (statusCode) in if (statusCode == .dispatched) { print("Message sent to peer: \(peer.peerID)") } else { print("Failed to sent the message with error code: \(statusCode)") } })
Provide a MessageDeliveryStatusBlock
, which will notify you about the status of the message.
Messages can only be sent to currently discovered peers while both are online. The delivery of the message is best-effort, the API limited to 20 messages per second and cannot exceed the size of 100KB.
Receiving messages
To receive messages implement the following delegate method.
-(void)messageReceived:(nonnull NSData*)message fromNearbyPeer:(nonnull PPKPeer*)peer { NSString *messageAsString = [[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding]; NSLog(@"%@ has sent a message: %@", peer.peerID, messageAsString); }
func messageReceived(_ message: Data, fromNearbyPeer peer: PPKPeer) { let messageAsString = String(data: message, encoding: .utf8) print("\(peer.peerID) has sent a message: \(messageAsString)") }
Messages are transferred over our cloud, however, they are not end-to-end encrypted. Hence we recommend not sending any sensitive data through this means.
Documentation
For more details and further information, please refer to the documentation or check out the annotated P2PKit.h
header file.
License
By using p2pkit you agree to abide by our Terms of Service, License Agreement and Policies which are available at the following address:
http://p2pkit.io/policy.html
Please refer to "Third_party_licenses.txt" included with p2pkit for 3rd party software that the framework may be using. You will need to abide by their licenses as well.