Multipeer Connectivity Framework in IOS
All mobile application developers sometimes needs to implement some network interaction between multiple device so iOS mobile application development is not an exception. It’s fair to say that, dealing with networking is not an easy task for the beginners, especially if someone doesn’t have time to understand the basics. Luckily, Apple offers special framework to cover all basic connectivity features. In this blog post, we are aimed to provide all understanding of Multipeer connectivity with the code. This blog post is a “Starter Kit” for the Multipeer connectivity in swift. All provided code are testing and we are going to use it in real project.
As per the Apple’s official documentation:
"The Multipeer Connectivity framework supports the discovery of services provided by nearby devices and supports communicating with those services through message- based data, streaming data, and resources (such as files). In iOS, the framework uses infrastructure Wi-Fi networks, peer-to-peer Wi-Fi, and Bluetooth personal area networks for the underlying transport. In macOS and tvOS, it uses infrastructure Wi-Fi, peer-to-peer Wi-Fi, and Ethernet.” (This blog post will be focused on iOS development side of framework.)
In the case of iOS development, Multipeer connectivity framework allows to connect multiple device in local area. It will allow to create connection, define participant, claim participation and finally performa the exchange of data between multiple devices. Basic parts can be represented as session, peer, browsing and advertising. Let me explain them in more detailed.
Session : The Object of MCSession class will be used to handle the communications between devices. For example sending files or start the stream.
Peer : The object of MCPeerID is first the first thing to initialize with it’s property name displayName which will be available to other connected devices.
Browser : The object of MCNearbyServiceBrowser class is represented as a browser. Method of this class allows to initiate search and invite other devices.
Advertiser : The Object of MCNearbyServiceAdvertiser and MCAdvertiserAssistant allows devices to advertise themselves and indicates that others can invite them for a session.
The complete flow consists of two phases. 1) Session and 2) Discovery. Browsing and advertising are belongs to the discovery. Developers can use discoveryInfo to provide and data before connection get established.
To get the required session, at least one device have to enable browsing and others have to enable the advertising. All the device can also keep both modes as an active mode as well. Once the invitation will be accepted the session will be established between devices to send/receive streams, data and resources.
The device can accept the invitation automatically or after user’s permission. Obviously it depends on the requirements. There is also standard UI available for the same. MCBrowserViewController supports basic indication and invitation feature.
All the events like connection, discovery, disconnection and data transfer will be handled by delegate methods. If the app will be move into the background, framework will stop browsing and advertising and it will be restored once app returns to the foreground. Developer can manage the reconnection of that session.
Application Fundamentals
After understanding of the main concepts, we can have a look at the basis for the application. Let us assume that separate file like ConnectionManager handles the connectivity.
import MultipeerConnectivity
Next, it’s important to implement four instances which we have discussed above:\
var session: MCSession!
var peer: MCPeerID!
var browser: MCNearbyServiceBrowser!
var advertiser: MCNearbyServiceAdvertiser!
Also we have to store the list of all the discovered peers.
var allPeers = [MCPeerID]()
In order to implement all the functionality, we have to conform some protocols. Every
protocol name will explain what it does.
class ConnectionManager: NSObject, MCSessionDelegate,
MCNearbyServiceBrowserDelegate, MCNearbyServiceAdvertiserDelegate
Now we have to provide the identification of devices, and it should be done at the time
of peer initialization. In our case, we are using device name but basically it should be
upon your requirements.
override init() {
super.init()
peer = MCPeerID(displayName: UIDevice.currentDevice().name)
}
Here we can create an object of session using peer and assign the delegate of session.
override init() {
...
session = MCSession(peer: peer)
session.delegate = self
}
Now the turn of browser:
override init() {
...
browser = MCNearbyServiceBrowser(peer: peer, serviceType: "music-play")
browser.delegate = self
}
Browser needs peer as a parameter then it takes serviceType. It’s necessary to keep
noted that this string can’t be changed after initialization of the browser object. By
setting up this string, we are instructing the browser which connections needs to be
searched. These information will be provided by the advertisers. There are some strict
rules about the naming like string should be created by less then 15 characters and it
should contain lower case ASCII characters, hyphens and numbers. The rules needs to
be followed otherwise runtime crash will occur.
Finally advertiser:
override init() {
...
advertiser = MCNearbyServiceAdvertiser(peer: peer, discoveryInfo: nil, serviceType:
"music-play")
advertiser.delegate = self
}
So far init function of class should have the following appearance:
override init() {
super.init()
peer = MCPeerID(displayName: UIDevice.currentDevice().name)
session = MCSession(peer: peer)
session.delegate = self
browser = MCNearbyServiceBrowser(peer: peer, serviceType: "music-play")
browser.delegate = self
advertiser = MCNearbyServiceAdvertiser(peer: peer, discoveryInfo: nil, serviceType:
"music-play")
advertiser.delegate = self
}
Browsing Implementation
Now let’s move to the main fundamental part. Below functions will reflect delegate functions for the Multipeer connectivity Lifecycle.
withDiscoveryInfo info: [NSObject : AnyObject]!) {
allPeers.append(peerID)
}
The above function will be called once some peer will get discovered. First we will keep that peer in the peer array for the future actions. Next, we can handle events according to our requirements.
If application can found the peer then it can also lose the peer as well. For this case, protocol have below function
If application can found peer, it can lose it as well. For this case, protocol contain:
for (idx, onePeer) in enumerate(allPeers){
if onePeer == peerID {
allPeers.removeAtIndex(idx)
break
}
}
}
We have removed the lost peer from the array. Now we can say that it’s impossible to
assume the modern software development without error management. Here is the
basic example of how we can manage the errors.
func browser(browser: MCNearbyServiceBrowser!, didNotStartBrowsingForPeers error:
NSError!) {
println(error.localizedDescription)
}
We have the connection functionality in a separate file of ConnectionManager so let’s create an instance of that class in AppDelegate that is by-default available in every project.
var connectionManager: ConnectionManager!
Do not forget to initialize it :
func application(application: UIApplication, didFinishLaunchingWithOptions
launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
connectionManager = ConnectionManager ()
return true
}
Also in a ViewController, let’s provide the access to connectionManager object.
func viewDidLoad() {
...
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.connectionManager.browser.startBrowsingForPeers()
}
By having the above code we can make the application start browsing for other devices after User interface has been loaded. The only thing that we need to do here is stopBrowsingForPeers() that will stop the browsing of peers.
Advertising Implementation
As you can understand, without advertising browsing will become useless. So in this example application will browse and advertise itself continuously. Start advertising is as easy as browsing.
Function stopAdvertisingPeer() will stop process.
Invitation.
The main goal of this Multipeer connectivity framework is to implement the data exchange between devices. Once the device will find other devices then it’s the time to move at the final step of first phase. In our example, invitation will be send automatically after device will be discovered by the browser. However, in the real project it is up to the specific requirements.
To invite the the peer after it gets discovered, we have to edit one of the delegate methods, which is given below.
withDiscoveryInfo info: [NSObject : AnyObject]!) {
allPeers.append(peerID)
let anotherPeer = allPeers.popLast()
browser.invitePeer(anotherPeer!, to: session, withContext: nil, timeout: 35)
}
What we have done above? We have just extracted the last peer from our array of peers and put it inside the invitePeer(). We will provide the session object as well for the invitations to go in a same way. Also it requires to specify the timeout. Timeout defines that how much time inviter can wait for the response.
In the next steps, we will organize the behavior from the receiver side to implement the Next steps will organize behavior on receivers side. It is time to implement MCNearbyServiceAdvertiserDelegate and handle the invitation.
peerID: MCPeerID!, withContext context: NSData!, invitationHandler: ((Bool,
MCSession!) -> Void)!) {
let invHandler = invitationHandler
invHandler(true, self.session)
}
In above code the invitation will be accepted immediately.
MCNearbyServiceAdvertiserDelegate offers a method to handle start advertising failures. You can just print the error message in console to go in detail.
error: NSError!) {
println(error.localizedDescription)
}
Now let’s work with the session itself
First phase was the only preparations so far. Second phase is the session itself. There are three possible states : Connecting, Connected and Not Connected.
Now it’s the turn of MCSessionDelegate methods :
MCSessionState) {
switch state{
case MCSessionState.Connected:
println("Connected to session: \(session)")
case MCSessionState.Connecting:
println("Connecting to session: \(session)")
default:
println("Did not connect to session: \(session)")
}
}
Data Sending
The below function is a standard sending operation. It’s simple and proper example of data exchange inside the ConnectionManager Class.
Bool
{
let dataSend = NSKeyedArchiver.archivedData(withRootObject: data)
let arrayOfPeers = targetPeer
do {
_ = try session.send(dataSend, toPeers: arrayOfPeers, with:
MCSessionSendDataMode.unreliable)
} catch {
return false
}
return true
}
The framework accepts the information using NSData type for the exchange that is why we need a wrapper. NSKeyedArchiver is the best solution for that.
For the most cases, you can use unreliable mode. However, if your message delivery has a crucial influence then you can use reliable mode. Now in your ViewController you can send the data using appDelegate.connectionManager.sendData()
Receiving Data
The process is provided by MPCSessionDelegate. Inside ConnectionManager class you can just add :
MCPeerID!) {
// First you have to “extract” information
let extractedData = NSKeyedUnarchiver.unarchiveObject(with: data)
let receivedData = extractedData as! Dictionary<String, String>
// Now you can start working with your received Data
}
Conclusion
Multipeer Connectivity framework is very useful for performing the basic network activities in the project. Of course, you have to develop it as per your requirements. List of delegate functions listed here are not full but it will be enough for your basic work. XCode will indicate if anything is missing.
Comments
Post a Comment