iOS

Understanding Multipeer Connectivity Framework in iOS 7 – Part 1


Editor’s note: This is part 1 of the Multipeer Connectivity series.

The Multipeer Connectivity Framework is one of the many new frameworks iOS 7 introduces, and it broadens the range of the applications that can be built for the new operating system. Its aim is to enable developers to create applications that will allow devices being in close distance to get connected, simply using Wi-Fi networks or Bluetooth. Great tools are offered for establishing communication between nearby devices, and for exchanging data and other resources without much effort to be required.

Continuing this introduction, it’s necessary to discuss a few more details about the Multipeer Connectivity framework and become more familiarized with it before we see any example of it. First of all, it’s really important to underline that this framework works on nearby devices only, meaning devices that use the same network infrastructure (such as Wi-Fi or Bluetooth), so don’t expect it to work for devices existing in long distance. With Multipeer Connectivity, one device is able to get connected to many other devices and communicate at the same time with all of them. A single device is called a peer. What actually takes place when a connection between two peers is established, is that a Multipeer Connectivity Session gets created, and this session is responsible for managing the whole communication and data exchanging between peers. For multiple devices, multiple sessions can be set up.

multipeer connectivity featured

Prior to any session establishment, devices (peers) need to discover one each other. Discovery is the first phase that takes place when Multipeer Connectivity is used in applications. How discovery is being made? Let’s suppose we have only two devices we want to get connected. One device at least must work as a browser, in order to search for other existing devices, and the second device must be discoverable by telling that it’s out there and wants to connect to someone, or in other words the second device must advertise itself. Normally, both devices should advertise themselves, but at least one should browse for other peers in order to establish a connection.

Regarding the browsing functionality especially, Apple provides two ways to use it. The first and easy one, is a browsing UI built-in directly into the framework, which when is called a modal view is displayed listing all available and connected devices to the one that works as a browser. The second way offers greater flexibility to developers, as it’s a totally programmatic way, so one can implement customized browsing according to the needs of the application. Later on we will use the first way only, as this is an introductory tutorial about this framework.

Once the discovery of other peers has been done, the peer that locates the advertiser sends a message asking to establish a connection. If the second one (the advertiser) accepts that, then a session is created and both peers are ready to exchange data.

Talking about data, there are three kinds of data that can be sent and received using the Multipeer Connectivity framework. These are:

  • Messages (including text, images, and everything else that can be converted to a NSData object)
  • Streaming
  • Resources

Going into some more details about messages, there are two modes that can be used for data transmission: The reliable and the unreliable mode. Using the reliable mode to send data, the framework makes sure that everything will arrive to the receiver, and in the order been sent. The cost of this reliable data sending though, is that is required more time for the transmission to be completed. On the other hand, when using the unreliable mode, data is send in no-time, really fast, but there is no guarantee that everything will arrive to the other peer, and of course, data is not sent in order. Which way is the most preferable clearly depends on the requirements of each application, so it’s up to every developer to decide what mode will use.

Generally, the Multipeer Connectivity framework offers classes and libraries for high-level development, so no C or other kind of low-level programming is required. Under the hood, many features are built-in ready to be used, taking away any hassle caused by implementing or addressing any network-related issues. In this tutorial we won’t consume any more time in theoretical presentation of the framework, therefore you are strongly advised to visit Apple’s documentation and do some study, and of course, watch the WWDC 2013 Session 708 video. Note that for testing the Multipeer Connectivity framework you need to have at least two devices, or use one device and the iPhone Simulator.

To see how easily the Multipeer Connectivity feature can be put in action and how it lets devices communicate, just keep reading!

Demo App Overview

In this tutorial we will create a sample application for demonstrating the most important aspects of the Multipeer Connectivity framework. I mentioned previously that there are three kinds of data that can be exchanged using it: Messages, streaming and resources. In the demo application that we are going to implement, we will see how to send messages, meaning NSData objects from device to device, and how to share resources, such as files. However, before we start building, we need to take a quick look on what exactly the demo app is about.

We are going to create a tabbed application, with three tabs in total. Starting on purpose from the last one, titled My Connections, we will create a view controller to manage the peer (device) naming, the advertising and all the connections (the sessions actually) of the device. Specifically, using a text field, we’ll be able to set a custom name for the device that will appear to other peers. A button will display the default browser UI when it’s tapped, while a switch control will toggle the advertising functionality of the device. In a table view they will be listed all of our connections. Under the table view there will be one more button, which will be used to disconnect the device from the session. The next figure presents a synopsis of the view controller:

Multipeer Connectivity Demo App1

Behind the first tab is going to be another view controller. This one will be titled Chat Box and it will be used for sending text messages among devices using the Multipeer Connectivity. It will consist of a text field where the message will be written, a text view where the whole chat will appear, and a couple of buttons, one for sending the message and one for cancelling it. Here is a screenshot of it:

Multipeer Connectivity Demo App2

Lastly, one more view controller is going to lie behind the second tab, titled File Sharing. Here a table view will contain a list of some (sample) files. When a row is selected, then an action sheet will be displayed with all the peer names as its options. By selecting a peer, the selected file will be sent directly to it. To make it more attractive and more interactive, the file name, the sender and a progress view showing the progress of the file being received will be shown to the last row of the table view when a device receives a file. When it sends one, then right next to the file name a percentage showing the progress of the sending will appear. Here is the view controller behind the second tab in its initial state:

Multipeer Connectivity Demo App3

I presented all the view controllers of the demo application starting from the third tab in purpose, as the view controller behind it is the most important for us in this tutorial. Through it, we will see how the discovery and session establishment phases are handled and managed.

Before we reach the end of this brief app presentation, there is something more quite important I necessarily need to say. In order for the Multipeer Connectivity options to be app-wide and in order to avoid repeating building the same functionality three times (for each view controller separately), we will create a class for managing all the framework-related objects and tasks. In the application delegate (AppDelegate) class then we will initialize an object of this class. Also, through the application delegate we will access this object across the application. After all, it’s a terrible programming practice to develop something multiple times, instead of build it just once and use it as a tool.

So, after having walked through the application we are going to develop, it’s time to start working on it. You may download the two sample files at the end of this tutorial if you want to build step by step. Alternatively, get the full demo application provided for your own convenience, but stay put for all code details.

Demo App Creation

Launch Xcode and create a new project by clicking on the respective button at the left side of the Welcome screen:

Xcode Welcome Dialog

At the first step of the guide, select the Tabbed Application option in the Application category, under the iOS section.

Xcode New Project Template

Click on the Next button to move to the next step of the guide. I named the app MCDemo in the Product Name field, but feel free to name it otherwise if you’d like. Besides that, make sure that in the Devices drop down menu, the iPhone option is selected. Leave everything else as it is and proceed by clicking on the Next button.

Xcode Project Options

Finally, select a destination for your project and click on the Create button to end this guide. The project is now ready.

Adding New Tab

As I have already said, the application will consist of three tabs in total, but by default there are only two. So, our first task is to create a new tab and to connect it with a new class we will add to the project. Also, further than that we will set all tab titles and icons.

Begin by adding a new UIViewController class to the project for the third tab that we will add in a while. On the Project Navigation, Ctrl-click or right-click on the MCDemo group and from the popup menu select the New File… option, as shown in the next figure:

Multipeer Connectivity - Add New File

Under the iOS section, make sure that the Cocoa Touch category is selected. Then, select the Objective-C class icon and click on the Next button to proceed.

Multipeer Connectivity - Add New File

In the Class field, set the ConnectionsViewController value. Also, make sure that the UIViewController value is filled in the Subclass of field. Do not check any of the other two options. Click on the Next to move to the next step.

Multipeer Connectivity - Add New File

At the last step click on the Create button and you are ready. You should now see the ConnectionsViewController.h and ConnectionsViewController.m files on the Project Navigator.

Multipeer Connectivity - Add New File

Click now on the Main.storyboard file to let it open on the Interface Builder. As a first step, add a new view controller, by dragging it from the Object Library and dropping it to the canvas, right below the second view controller scene.

Multipeer Connectivity - Designing Interface

Next, keep the Ctrl key on the keyboard down, click on the Tab Bar Controller scene, and without leaving the mouse button drag the mouse on the new scene we just added to the interface. Release both the Ctrl key and the mouse button. A black, popup window will appear, where under the Relationship Segue section you must click on the view controllers option, so as the new view controller to be added as a tab to the tab bar controller.

Multipeer Connectivity - Designing Interface

The tab bar controller now contains three tabs, and our new scene has been connected to it. We need now to set the ConnectionsViewController class we added earlier as the class for the new view controller. To do so, click on the new view controller scene, and in the Utilities Pane, open the Identity Inspector and in the Custom Class section start writing Connect… in the Class field. Xcode will automatically complete it for you.

Multipeer Connectivity - Set Class Name

Finally, we just have to set the correct titles to the tabs. To set a tab’s title, you must first select the tab on the scene (and not on the tab bar controller) you want to modify. Then, go once again on the Utilities pane, open the Attributes Inspector and under the Bar Title section set the title in the Title field.

Multipeer Connectivity - Set Title

Set the following titles:

  • First View Controller: Chat Box
  • Second View Controller: File Sharing
  • Connections View Controller: My Connections

Note that any visual configuration on Interface Builder made in this tutorial is targeted for 4″ screen devices, such as iPhone 5 or 5S. If you want to make the application run on older devices with a 3.5″ screen size, just play around by applying the suggested constraints by Xcode and you’ll easily manage to have it running in these devices as well.

Connections View Controller: Setup the Interface

Now that the third tab, along with its respective view controller have been added, it’s time to start building our application beginning from the view controller of the last tab, the Connections View Controller. Our first mission is to setup its interface, as well as to declare and connect any required IBOutlet properties and IBAction methods. So, while being in the Interface Builder, drag and drop the controls presented right next from the Objects Library into the Connections View Controller scene. Note that for each control are provided all the properties you should modify:

  1. UITextField
    • Frame: X=20, Y=20, Width=280, Height=30
    • Placeholder: The device name displayed to others…
  2. UILabel
    • Frame: X=20, Y=63, Width=180, Y=21
    • Text: Visible to others?
  3. UISwitch
    • Frame: X=251, Y=58, Width=51, Y=31
    • State: ON
  4. UIButton
    • Frame: X=94, Y=92, Width=132, Y=30
    • Title: Browse for devices
  5. UITableView
    • Frame: X=0, Y=130, Width=320, Y=352
  6. UIButton
    • Frame: X=121, Y=490, Width=78, Y=30
    • Text: Disconnect
    • Enabled: False (Unchecked)

After adding all the above, here is how your scene should look like:

Multipeer Connectivity - UI

Let’s declare now some IBOutlet properties and some IBAction methods. Open the ConnectionsViewController.h file, and add the next properties in the interface body:

Also, add these IBAction methods:

Fine. Now head back to the Main.storyboard to connect them to the appropriate controls. To do that, make sure that the Document Outline Pane is shown. Next, Ctrl-click or right-click on the Connections View Controller – Connections object, and a black, popup window will appear. Next to each property (and each IBAction method) there is a circle. Click on this circle next to an IBOutlet property, and without releasing the mouse button, drag and drop to the respective control on the scene. The following figure illustrates exactly that:

Multipeer Connectivity - Setup IBOutlet

The connections should be done as follows:

  • The txtName property goes to the UITextField object.
  • The swVisible property goes to the UISwitch object.
  • The tblConnectedDevices property goes to the UITableView object.
  • The btnDisconnect property goes to the second UIButton (titled Disconnect) object.

For the rest objects on the scene we don’t need to use IBOutlet properties.

Using the same method as above, connect the declared IBAction methods to the appropriate controls. Here is how you should match the methods with the controls:

  • The browseForDevices: method should connect to the first UIButton (titled Browse for devices) object.
  • The toggleVisibility: method should connect to the UISwitch object.
  • The disconnect: method should connect to the second UIButton object.

The interface for the Connections View Controller is now ready. We will discuss about the functionality and the purpose of each control we just added later. For the time being, we have to focus for first time on the Multipeer Connectivity framework details.

A Multipeer Connectivity Framework-Related Class

Right now we are going to do a big turn, as we will leave behind us the Interface Builder and any visual setup, and we will focus totally on code. In this section our goal is to create a new class, in which we will implement all framework-related logic, we will use any required framework classes and we will perform any necessary tasks. Note that it’s not required by the framework to do so, however this is the most convenient way for this sample application to perform app-wide tasks without having to re-write the same code more than once.

Before doing all these, the new class must be created, so Ctrl-click or Right-click on the MCDemo group in the Project Navigator, and from the popup menu select the New File… option, or simply press Command-N on your keyboard.

In the guide that appears, select the Objective-C class option as the template for the new file. Click Next to proceed. Next, at the Subclass of field, set the NSObject value. Right above, in the Class field, you must write the name of the new class. I named it MCManager, and it would be a good idea you name it the same, just to follow properly this tutorial.

Multipeer Connectivity - Create new class mcmanager

Finally, click on the Next, and then click on the Create button to add the new class into the project. The MCManager.h and MCManager.m files should now be listed on the Project Navigator.

Let’s write some code now. Open the MCManager.h to declare some required objects. Before doing that however, we must import the Multipeer Connectivity library to our project, therefore head at the top of the file and add the next one:

As you see, I didn’t add the framework manually to the project, but I directly used it. The compiler will do that for us, thanks to a new feature that incorporates, named Auto Linking.

Now, modify the interface header line to adopt the MCSesssionDelegate protocol as follows:

After doing so, declare the next objects:

The peerID object represents the device and it contains various properties needed for the discovery and session establishment phases. The session object is the most important one, as it represents the session that the current peer (the device that this app will run) will create. Any data exchanging and any communication details are controlled by this object. The browser object is actually representing the default UI provided by Apple for browsing for other peers, and we will use it for this purpose. For more advanced handling of the browsing feature of the framework, Apple provides a programmatic alternative way, but for now it’s out of the scope of this tutorial. Finally, there is the advertiser object, which is used from the current peer to advertise itself and make its discovery feasible.

Notice that the classes of all these objects belong to the Multipeer Connectivity framework. You’ll see how we will use them, but for now let’s declare some public methods that we’ll need later as well:

Now head to the MCManager.m file, and for starters add the next init method for initialising our class:

At this point, Xcode should normally be complaining and displaying some warnings. Those are because we haven’t implemented the public methods yet, and also because we didn’t add any delegate methods of the MCSessionDelegate protocol. Let’s silence all of them, first by adding all necessary delegate methods:

Even though it’s obvious by the delegate method names what they are all about, let me make a quick reference on them. The first method is called when a peer changes its state, meaning when it’s connected or disconnected. There are three states: MCSessionStateConnected, MCSessionStateConnecting and MCSessionStateNotConnected. The last state is valid even when a peer gets disconnected from our session. Generally this delegate method is called when the peer state is modified. The second delegate method is called when new data arrives from a peer. Remember that three kinds of data can be exchanged; messages, streaming and resources. This one is the delegate for messages. The next couple of methods are called when a resource is received, and finally the last one is invoked for incoming streams. In this tutorial we’ll use all of them, except for the last one.

Let’s go to the public method implementation now. Begin with the next code fragment, and we’ll talk about it in a while.

First of all, the peerID object is initialised, as everything is based on it. The displayName string value that is provided to the init method (and as parameter to the method) concerns the name of the device as it will appear to other peers. This can be the device’s name (such as ‘John’s iPhone’), or a custom name. If you recall, we added a text field in our interface for this purpose, and we’ll see more about it later.

Next, we initialise the session object, the most important one as everything depends on it. We provide it with the peerID for initialisation and we make our class its delegate in the last line.

Let’s continue:

This method contains just one, but an important line. It is the initialisation of the default, pre-made by Apple view controller that displays a browser for searching other peers. It accepts two parameters upon initialisation: The serviceType defines the type of service that the browser should look for, and it’s a small text that describes it. This text should be the same for both the browser and the advertiser in order for the first one to be able to discover the second. There are two rules about its name:

  1. Must be 1–15 characters long.
  2. Can contain only ASCII lowercase letters, numbers, and hyphens.

The second parameter is the session object initialised on the previous method.

One more public method to go:

We are going to use this public method for toggling the advertising feature of the device. As you notice, the parameter defines whether the device should advertise itself or not, depending on the settings of our app. Remember that we added a UISwitch object for making our peer visible/invisible to others.

When we want the advertiser on, we initialise it and we start it. Note the serviceType text that matches to the browser’s one. When we want to turn off the advertiser, we simply stop it and we make our object nil.

So, for now our class is ready to be used as a tool. We’ll visit it many times along the way, as we’ll add code to the delegate methods of the session. Maybe not everything makes sense to you, but don’t worry, all will become clear upon usage.

One last thing we have left to do, and that is to go to the AppDelegate.h file and declare an object of our class:

Don’t forget however to import the class:

After that, open the AppDelegate.m file and inside the application:didFinishLaunchingWithOptions: delegate method add the next initialising line:

We are now in position to use our class through the object of the application delegate. Let’s go ahead to put Mutlipeer Connectivity in action!

The Discovery Phase

The browser view controller is going to be displayed through the ConnectionsViewController class we previously created, therefore we need to make it conform to the MCBrowserViewControllerDelegate protocol in order to be able to handle the browser. Go to the ConnectionsViewController.h file and import the Multipeer Connectivity framework, just like you did in the MCManager class:

Next, modify the interface header as follows:

I have already mentioned a couple of times that we will use our custom class (the MCManager class) through the application delegate. That means that we need access to it, so go to the ConnectionsViewController.m file, right into the private section of the interface. In there declare the next object:

Don’t worry if Xcode shows an error. You just have to make the following import:

Now we can access the mcManager object of the application delegate. How? To see that go straight ahead to the viewDidLoad method. In there we will add three lines:

In the first line we instantiate the appDelegate object using the sharedApplication class method. After doing so, we are able to call any required public methods of the mcManager object, and that’s exactly we are doing right next. The setupPeerAndSessionWithDisplayName: method is being called, and as we are in the initialisation of our class, as the display name of our device we specify its actual name. During execution, if no custom name is set to the text field, then this name will appear to the other peers. Finally, we call the advertiseSelf: method with the current state of our switch, to either enable or keep nil the advertiser object.

Let’s implement the browser view controller appearance now, by adding the necessary code to the browseForDevices: IBAction method. Add the next code fragment in your ConnectionsViewController.m file:

Using the first line, we call the setupMCBrowser public method of the MCManager class. Next, we set self (this class) as its delegate and finally we modally present it. After having this method implemented, the button with title Browse for devices is going to perfectly work, and if you compile and run the application and tap on that button, here is what you should expect to see:

Multipeer Connectivity - Browser Running

Great! We just took our first taste from the Multipeer Connectivity framework. However, if you try to use the Done or Cancel button of the view controller, you’ll find out that they are not working. To enable them, we must implement two delegate methods of the MCBrowserViewControllerDelegate. So, turn back to Xcode and add these two methods:

When tapping on the Done button of the view controller, we simply want to dismiss it, and similarly want the browser to act when the Cancel button is tapped. Therefore, both of these methods contain the same code, just for dismissing the browser view controller. The first method is the delegate for the Done button, and the second one is the delegate for the Cancel button.

Before going any further, just note how we access the browser object using this:

Now, both buttons of the browser view controller are perfectly working. However, the Done button is disabled and you cannot test it yet, unless another device is discovered and gets connected to the current one. Before we see that though, let’s implement two more functionalities: How to set a custom name for the device and how to enable/disable the advertiser.

Peer Display Name And The Advertiser State

Let’s start from the text field. Go to the ConnectionsViewController.h file and adopt the UITextFieldDelegate protocol, as shown in the next line:

Now, in the viewDidLoad method inside the ConnectionsViewController.m file, add the next one:

We will implement the textFieldShouldReturn: delegate method of the text field, because we want the keyboard to be disappeared when the Return button is tapped and the peerID object of the MCManager class to get the name we set to the text field. However, in the viewDidLoad method we have already initialised both the peerID and the session objects, so first we need to set them to nil and then reinitialise them using the specified name by calling the setupPeerAndSessionWithDisplayName: method. Here is the implementation:

Note that we check if the advertiser is on, and if that’s the case we first stop it and then we set the respective object to nil.

There is an important fact that I should point out here. Once our device is connected to another peer, we shouldn’t change its display name, as a session would have been already established and any data exchange might be in progress. Therefore, in order to protect us from changing name while the device is connected, we will keep the text field disabled. Only when there is no connection to other peers the text field will be enabled, and we are going to associate this functionality with the Disconnect button later on. More specifically, once a connection gets established, the text field will become disabled, and when the Disconnect button is used to stop a connection, it will become enabled again. So, for now, just keep this in mind and let’s move forward.

Let’s make the switch control now to enable and disable the advertiser. Implement the following IBAction method:

Pretty simple implementation, right? We just call the advertiseSelf method to set the advertiser’s state according to the switch’s state.

If you test the application now (in two devices or in one device and in the Simulator), you can play around with the peer discovery and the two more functionalities we added here. Here are some screenshots:

When a nearby device is found:

Search for Nearby Devices

Using the switch to turn on/off the advertising feature of the second device:

Multipeer Advertising

Changing the display name of the device:

Multipeer - Changing Device Name

Note that the display name of the device, as well as the advertiser’s state can be changed, as we still haven’t made any connections. In the next part we are going to do exactly that. We will also see how we can get notified about the connected peers, how to show the other peer’s display name on the table view and how to make the Disconnect button work.

Making a Connection

Making a connection is really simple. In the browser view controller, you just have to tap on a nearby device’s name and wait until it gets connected. The view controller displays the status of the connections, changing from the Not Connected to Connecting and finally to the Connected state. Once this happens, the Done button becomes enabled.

Multipeer - Making Connection

On the other device an alert view similar to the next one appears, prompting the user to accept or decline the connection:

Multipeer - Accept Connection

Once the user taps on the Accept button, the connection gets established. The important now is how we can handle it, and first of all, how we can be notified about the other peer connection state all the time. Thankfully, the Multipeer Connectivity framework gives us some MCSession delegate methods, which we implemented previously in the MCManager class, without having written any code though.

So, open the MCManager.m file and locate the session:peer:didChangeState: method. This one is called when a new connection takes place, and is our job to handle the info that provides. What we are going to do in our case is quite simple: As this is a class other than the one that we use to manage our connections (the ConnectionsViewController class), we will post a notification to let the latter know about the peer state changing, and along with the notification we will post all the provided info. Let’s see the implementation:

At first, we create a NSDictionary object and we set the peerID and state parameter values as its contents. Next, we post a notification with the MCDidChangeStateNotification name and the dict object as its user info dictionary. By doing all these, we make sure that every time a peer’s state gets changed, our class will know it.

However, for the time being, only the half job has been done. We need to make the ConnectionsViewController class to observe for this notification and act appropriately when one arrives. So, open the ConnectionsViewController.m file, and head to the viewDidLoad method. Add the next content to it:

With this command, we let our class observe for the specific notification. When such a notification arrives, a method should be called and any actions that need to be taken, should be written in that method. In our case, this is the peerDidChangeStateWithNotification:, and it’s a custom private method that we will declare right now. Go to the private section of the interface, and add the next declaration:

Next, we must implement it, but let’s discuss about it for a moment. What do we want to do when a new connection is being made? Simply to add the connected peer’s display name to the table view, to enable the Disconnect button and to disable the text field (I explained earlier why). On the other hand, when a peer is disconnected, we simply have to remove its name from the table view, and if no other peers exist, to disable the Disconnect button and enable the text field.

In order to do all that, we need an array that will be used as the datasource for our table view. Therefore, let’s declare and initialise this array, and then we will be able to implement the private method.

Go once again at the private section of the interface and add the next declaration:

Then, in the viewDidLoad method initialise it:

Also, in order our table view to respond to everything we want, we must set our class as its delegate and its datasource. So, while being in the viewDidLoad method, add the next two lines:

Xcode will issue now some new warnings for these two commands. That’s because we haven’t adopted two required protocols, the UITableViewDelegate and the UITableViewDatasource. To fix this, go to the ConnectionsViewController.h file, and modify the interface header line, just as follows:

Now we are totally ready to implement our private method that will be called when a notification arrives. Open once again the ConnectionsViewController.m file, and start with these lines:

What we do here is not hard to be understood. We just get the two objects from the user info dictionary (peerID and state), and we keep the display name of the connected peer to a NSString object. Let’s keep going:

First of all, we perform any action only when the current state is other than the MCSessionStateConnecting. So, if currently the state matches to the MCSessionStateConnected, then we add the peer display name to the arrConnectedDevices array. Otherwise, if the state is equal to the MCSessionStateNotConnected one, then we find the index of the current peer in the array and simply remove it. This code snippet is the heart of our method. However, we are not finished yet. Add what is missing:

At first, we reload the data on the table view. After that, we check if there are any peers left connected, and we assign the result of this comparison to a BOOL value. Note that the connectedPeers method of the session object returns an array with all the connected peers to the session. Anyway, depending on the bool value, we set the text field and the disconnect button enable state.

The method is ready, but if you run the application you’ll see no results at all. Why not? Because we still haven’t implemented the minimum required table view delegate and datasource methods. Let’s do it now. Right next, you are given all these methods at once:

There is nothing hard here that needs to be explained. We just display the contents of the table view. If you test the app now and make a connection, you should see something similar to this:

Multipeer - Peer Connected

Finally, there is just one more thing to be done and our view controller will be fully functional. That is to make the disconnect button work. Simply, implement the disconnect: method:

As you see, the session object has the disconnect method, that enables it to break the connection. After that, the rest is easy to be understood.

Go and test once again the application. After you have a connection established, tap on the Disconnect button and you’ll see that in both devices the other peer’s display name goes away, while the text field becomes enabled again.

At this point, the Connections View Controller is ready! Up to now, we have seen many things regarding the framework, but let’s go ahead for even more interesting stuff!

Setup The Chat Interface

Once a connection is established, we can exchange any allowed data we want. In this part we will work with setting up the First View Controller interface, and later we will implement the chatting feature of the application. So, let’s get started!

Click on the Main.storyboard file to bring the Interface Builder up. Go to the First View Controller scene and delete its default contents.

Xcode Storyboard Delete Default Content

Then, drag and drop the following controls from the Objects Library and set some of their properties as given below:

  1. UITextField
    • Frame: X=20, Y=20, Width=280, Height=30
    • Placeholder: Your message…
  2. UIButton
    • Frame: X=254, Y=58, Width=46, Height=30
    • Title: Send
  3. UIButton
    • Frame: X=25, Y=58, Width=48, Height=30
    • Title: Cancel
  4. UITextView
    • Frame: X=0, Y=96, Width=320, Height=422
    • Text: None
    • Background Color: Light Gray

Your scene should now look like this:

Multipeer Demo - First VC Scene

Let’s declare a couple of IBOutlet properties now. Open the FirstViewController.h file and add these two inside the interface body:

Also, we want an IBAction method for each button, so add these as well:

Before we go back to Interface Builder and while being in this file, adopt the UITextField protocol, as we will need later to implement a delegate method of it.

Following the same steps for connecting the IBOutlet properties and the IBAction methods as we did while we were setting the Connections View Controller up, go and connect the txtMessage property to the text field and the tvChat property to the text view. Next, connect the sendMessage: IBAction method to the Send button and the cancelMessage: to the Cancel button. Once you’ve done these steps, nothing else remains to be done here.

Let’s Chat

The title of this section speaks on its own for our goal here. We will do any required implementation, so our connected devices using the Multipeer Connectivity framework to be able to chat using text messages.

Let’s begin by doing something common for all three view controllers: To declare and instantiate an AppDelegate object. Go to the FirstViewController.m file, and import the AppDelegate.h file:

Then, inside the private section of the interface, make this declaration:

Finally, go to the viewDidLoad method and instantiate the object:

Now we are able to access the mcManager object of the application delegate. We are continuing by implementing the textFieldShouldReturn: text field delegate method. We need it because we want the Return button of the keyboard to behave just like the Send UIButton, and that is to send the message to other peers. However, before we see this method, and while we are still in the viewDidLoad method, let’s make our class the delegate of the text field:

Now we can implement the textFieldShouldReturn: method:

The sendMyMessage method we call here is a custom, private method that we will implement in a while and that it will do the sending job. Now, it’s a good chance to implement the two IBAction methods we previously declared:

They couldn’t be any simpler. The first one also calls the sendMyMessage method, and the second one just makes the keyboard go away, without sending any message at all.

Now we can dive in to the core of the desired functionality. We will implement the private method, and we will meet a new method of the framework that allows us to send messages.

Go to the private section of the interface to declare the method:

Now we may proceed to its implementation, so let’s see it first and we’ll discuss later.

The session object has the sendData:toPeers:withMode:error: method, which is the one that actually sends a message. The data should be a NSData object and that’s exactly is taking place in the first line by using the dataUsingEncoding: method.

The second parameter of the method must be a NSArray with all peers that should receive the message. For the sake of the tutorial, we’ll send the message to all connected peers. In a real application you can let users select the recipients of the message.

The third parameter is quite important, as this is the place where we define the mode that our data will be sent. At the beginning of this tutorial, I said that there are two modes, the reliable and the unreliable. This is supposed to be a chat application, so we don’t want to lose any package. Therefore, when calling the method we specify the MCSessionSendDataReliable value.

The last parameter is the all-time classic error object, which we use to check if any error occurred after we have this method called. Indeed, if any error occurs, we just log its description, as there is no reason to handle it in any other way in our demo app.

Finally, we perform three more tasks. First of all, we display the message to the text view, and to make clear that it’s us who wrote it, we prepend the I wrote: text. Next, we clear the text field from the current content, and finally we make the keyboard go away.

Further than the new method of the session object we just met, there is nothing difficult or weird. Let’s focus now on what happens when a message is received by a peer. Once that happens, the session:didReceiveData:fromPeer: delegate method is called, and it’s our responsibility to handle the received message according to our needs. We have implemented this method in the MCManager class, but with no content at all. Now it’s the appropriate time to pay it a visit and manage the received data.

Go to the MCManager.m file, and find the session:didReceiveData:fromPeer: method. Our approach here is going to be the same to the session:peer:didChangeState: method, and that means that we will post a notification that our class (FirstViewController) will catch and act appropriately.

Here is its implementation:

As you see, we create a NSDictionary object with the provided data and peer as its contents. Next, we post a notification with the given name and send the dictionary along with it. After having added this simple code, we have nothing more to do, so let’s go back to the FirstViewController.m file.

Our next task is to make our class observe for this notification, and we’ll do that in the viewDidLoad method. Add the next code fragment:

The didReceiveDataWithNotification: method in the selector is the private method that is called every time such a notification arrives. We are going to implement it in a while, but first let’s declare it. Go to the private section of the interface and add this declaration:

What we want this method to do is fairly simple. The user info dictionary of the notification contains the peer that sent the message and the message itself as a NSData object. From the peer object we’ll get its display name, and we will convert the data into a NSString object. Once that has been done, then we will add to the text view the peer display name along with its message.

Here is the implementation:

Note that we set the text to the text view by calling the performSelectorOnMainThread:withObject:waitUntilDone: method. This is done because the data is received in a secondary thread, and any visual update should always take place on the main thread of the application.

Our chatting feature is ready! If you want to test it, first establish a connection and then start sending text messages from peer to peer.

Multipeer Demo - Chat Sample

Summary

The Multipeer Connectivity framework is a brand-new feature on iOS 7. In this tutorial we gave you a brief introduction of the framework and demonstrated how you can use it to build a simple chat app. There is still a lot to explore. In part 2 of the tutorial series, we’ll continue to work on the project and see how to share file with nearby devices. Stay tuned.

For your reference, you can download the complete Xcode project from here. As always, feel free to share your thoughts and comments below.

Update: Part 2 is now available.

Tutorial
Getting Started with Fastlane for Automating Beta Testing and App Deployment
Tutorial
Introduction to MVVM: Refactoring a MVC App Using the MVVM Design Pattern
iOS
How to Access iOS Calendar, Events and Reminders Using Event Kit Framework
  • HiVoid

    HiVoidHiVoid

    Author Reply

    I followed the code, but I couldn’t find any devices when I tapped the button.(I am testing using two devices.) Then I downloaded the complete project from the author, the result is exactly the same… So I am really confused now, how does the author get the screenshot?…


    • Gabriel Thedoropoulos

      Hi,

      Please make sure that you have properly connected all IBOutlet properties and IBAction methods in the ConnectionsViewController class. That would be probably a reason for not discovering other devices.

      Gabriel


      • HiVoid

        HiVoidHiVoid

        Author Reply

        Thank you so much for replying me. I just went through the code again, and I completely understand the logic now. And I think it should work perfectly as your screenshots show. However, I am running YOUR project on my two devices, and there should suppose to be no errors at all. But neither of my device could find the other one when clicking the browse button. I am quite sure both devices have turned their wifi on, and both the switches are on. So I am really confused, and I want to wait for other guys to test the code to see if it can work out perfectly.

        One more thing, in the textfield, if I didn’t type anything in, and then i hit return button, the program just exit directly.


        • Gabriel Thedoropoulos

          Hey,

          You are welcome. That sounds really strange. I tested the project again after I’ve had it removed from both devices and the Simulator and it worked fine to me.

          Well, as a piece of advice I would suggest to remove and re-install the app once again, and also make sure that before you try to establish a connection you have the Connections tab selected (that’s important so all related objects to be initialized).

          Regarding the textfield, just add the next piece of code in the textFieldShouldReturn: delegate method and you’ll be fine:

          if (_txtName.text.length == 0) {
          return YES;
          }

          I just omitted it to make the presented code simpler.


          • Raj ambi

            Raj ambiRaj ambi

            Author

            i have used this code and its working fine. but its working only via bluetooth its not working via WiFi. Is anything possible to change/run through Wifi.please let me know as soon as.


    • Anmol

      AnmolAnmol

      Author Reply

      Hi Gabriel

      I am having the same problem as HiVoid is facing.Please help


  • tomski2000

    tomski2000tomski2000

    Author Reply

    Hi Gabriel,
    Great post. Is the any way to automatically send and receive invitations to peers that show up in the the list using MCAdvertiserAssistant? I would like to have the invitation process handled in the background for anyone with the same service type , and haven’t been able to get my head around the MCNearbyServiceAdvertiser.


    • Gabriel Thedoropoulos

      Hi,

      The MCNearbyServiceBrowser class has the

      invitePeer:toSession:withContext:timeout:

      method which I think you should check out. With it, you can programmatically invite other, discovered peers and wait for their response for a specific time period.

      For your convenience, here is the official documentation where you can find more info about your request.

      I wish good luck!


  • Ali

    AliAli

    Author Reply

    Hi Gabriel,
    Is there any way to advertise while the app is in the background? Right now the app stops advertising when goes into background state. Any low-level APIs i can use to achieve that?


    • Gabriel Theodoropoulos

      Hi,
      Unfortunately no, browsing and advertising in background is not supported. You must restart these services every time the app launches.


  • Anmol

    AnmolAnmol

    Author Reply

    Hi Gabriel

    Will this code work with a simulator and a real device connected on the same network.If yes, then i am probably facing the same issue as HIVOID is facing.Please help


  • raj

    rajraj

    Author Reply

    I don’t recall Apple doing this before, but they seem to have pulled access to the WWDC 2013 session videos months ahead of WWDC 2014.


  • raj

    rajraj

    Author Reply

    Hi Gabriel,

    I’ve tried both the code I typed in (up to before “Setup the Chat Interface”) and your finished project. In both cases, I get “Declined” when trying to connect to a discovered device, but I never see a prompt requesting a connection. I’m using the simulator and an iPhone 4 (iOS 7.0x), and I try to connect to the phone from the simulator.

    Any ideas why this might be? Obviously, it has something to do with settings (Bluetooth is ON for both my iMac and iPhone), not your code.


    • raj

      rajraj

      Author Reply

      So I tried using my iPhone and iPad Mini instead of the simulator and I was able to connect. I’m assuming that for whatever hardware reasons, the simulator cannot be used for multi-peer chat


      • appsunited

        appsunitedappsunited

        Author Reply

        Raj, thanks for this information! I was also confused, it’s not working with my iPhone 5S and the iOS Simulator on my Macbook Retina.


  • Sam

    SamSam

    Author Reply

    Hi Gabriel,
    Could you please tell me how to make it so in the UI TextView, the new messages stack on top of each other instead of going down the page? Thanks heaps. πŸ™‚


  • Apple Show

    Apple ShowApple Show

    Author Reply

    Is it possible to translate the view for the device connecting? Would be really awesome!


  • Amanda

    AmandaAmanda

    Author Reply

    Hi Gabriel,
    Is there any way to auto connect one specific device? For example when an iPhone named “A” acts as a browser and finds an iPhone named “B”, “A” can auto request for connection and then “B” will auto accept. After all these the two can chat with each other. Please help


    • Gabriel Theodoropoulos

      Hi there,
      It is possible to do what you’re asking, but you shouldn’t use the default browser view controller. You must use the MCNearbyServiceBrowser and the MCNearbyServiceAdvertiser classes, and implement a custom way to browse and advertise. I’d advise you to take a look in the Apple documentation. It’s not hard to be done.

      P.S.: There will be a tutorial soon for implementing custom browsing and advertising.


      • Amanda

        AmandaAmanda

        Author Reply

        Many thanks for your help! I’ll have a try. Looking forward to it!


  • Cool Fish

    Cool FishCool Fish

    Author Reply

    Hi Gabriel,

    Do I need extra code to handle reconnection after disconnecting? This demo project worked well for me until i started to disconnect from either side of a connection and then reconnect. The the advertiser seemed not to respond to the invitation from the same browser any more, and the browser showed “connecting” for ever. I tried to call the setupPeerAndSessionWithDisplayName() function inside disconnect(), but it did not solve the issue. Thanks!


    • Socraz6

      Socraz6Socraz6

      Author Reply

      I’m having the same issue.


  • s

    ss

    Author Reply

    thank you, this helped me a lot because i forgot to put performSelectorOnMainThread …now it works great tkanks


  • rickin

    rickinrickin

    Author Reply

    Hi Gabriel,
    First of all i would like to say its an outstanding piece of article. Although I am facing a strange issue, I could see multiple names appearing for the same device when the run your code. Can you please tell me what could be the reason for it ?


  • Mahender

    MahenderMahender

    Author Reply

    Hi Gabriel,

    I am not getting devices when I hit the Browse Devices. I tried your code also..Can u help me? I checked all the IBOutlets and IBActions(connections)..Thanks


    • Gabriel Theodoropoulos

      Hi,

      Almost after a year working with multipeer connectivity, and after having talked with many other developers, it seems that the MPC is “unstable”, meaning that you can’t say for sure if it will work or not. For example, when it works on my system and devices, it happens not to be working to my clients (occasionally). I’m afraid that I don’t have a clear answer for you, I’d just recommend you to turn off/on the WiFi and Bluetooth again to all your devices, and wait to see what will happen. If that won’t work too, then I’d tell you to take a look at the Apple’s documentation and implement the MPC manually (it’s easy to do).

      Wish you good luck!


      • bebo

        bebobebo

        Author Reply

        With the utmost respect for what you’ve done, do you think you could put a BOLD warning sign at the top of this blog that gives warning that this is an unstable approach?

        This way newcomers such as myself do not invest too heavily, as I am needing a robust bluetooth communication approach. thanks.


  • Adam Henry

    Adam HenryAdam Henry

    Author Reply

    Any advice for sending a jpeg using this method? I have tried everything I can think of to get this to send and receive something other than a txt file but to no avail. It won’t show up in the table.


  • zoey

    zoeyzoey

    Author Reply

    Hi I’m Zoey.Victoria. Vann and I’m new


  • zoey

    zoeyzoey

    Author Reply

    Hey! Umm? Do think I’m a nightmare, please?


  • Anthony Randazzo

    Great tutorial! Could you point me in the right direction toward customizing an MCBrowserController-like table to display devices/users nearby? – Thanks


  • B. Krishna Kishore

    Great Tutorial!!!! can any one help me on this? How can i send messages over long distance by relaying data from one node to another using multipeer framework? Kindly suggest me any ideas how to do this application?


  • Jung Jihoon

    Hi Gabriel, thank you for ur tutorial. I am just absolute beginner of programming.

    Well, having a small issue in “Connections View Controller: Set up the interface”, what to select one thing in received actions. see pic below.

    Please help me. πŸ˜€


    • Jung Jihoon

      and, same as others – disconnect, toggle…


  • Abner Abbey

    I think it’d have been much better that you had made a singleton class for the MCManager instead of putting is in the AppDelegate.


  • Mary Herno

    Mary HernoMary Herno

    Author Reply

    how many connections can this support?


  • yeah….1324

    Hello Gabriel,

    Thank you so much for the tutorial. I followed everything and it worked just fine. However, when I tried to connect two devices together, they won’t connect. The last step I could get working is the decline/accept window. Then it shows Connecting…., for a while and gone and nothing happened. So basically, after accepting the connection, the devices do not connect. I tried in your demo application too but still does not work. Thank you!


  • bebo

    bebobebo

    Author Reply

    why must this be developed only for the iphone? Isn’t it applicable to any iOS device? Also, I notice you’ve used ivars (_variableName instead of self.variableName). Why?


  • mbob

    mbobmbob

    Author Reply

    Thanks so much! I have an old iPad (I thought original, but perhaps iPad 2, as it’s running iOS 7) which I’ve wanted to connect wirelessly to my relatively new MacBook Pro (OS X 10.11.6).

    I wanted use Bluetooth, but I couldn’t get it to work. The old iPad only supports classic Bluetooth, not Bluetooth 4, or Bluetooth LE, and nothing I’ve tried works.

    So, for now anyway, I’ve given up on Bluetooth and am trying wifi. All of the projects I’ve tried to run also fail, because the newer environments don’t support iOS 7, except yours. Yours works!

    Getting it to build on Xcode 8 was not painless but eventually succeeded. However, I got this error when I tried to run:

    This iPad 2 Wi-Fi + 3G (CDMA) is running iOS 7.1.2 (11D257), which may not be supported by this version of Xcode.

    Googling the error message found this link
    http://stackoverflow.com/questions/39306688/any-way-to-install-app-to-iphone-4-with-xcode-8-beta

    and this advice:
    … You just need to copy DeviceSupport folder for iOS 7.1 from Xcode 7 to the new one. It’s located in:
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/7.1

    This advice worked for me.

    At present, I can communicate between the old iPad and my Mac running an iOS simulator, using your code. I’d prefer to communicate with a native Mac app on the mac, and I expect I can construct one, but do you have one — or any suggestions?

    Again, thanks so much!


  • Ganesh

    GaneshGanesh

    Author Reply

    Hi,
    Is it possible to broadcast message automatically to other devices without any button action to send that message.


Shares