iOS

Understanding Multipeer Connectivity Framework in iOS 7 – Part 2


Editor’s note: In part 1 of the multipeer connectivity series, we gave an introduction of the Multipeer Connectivity Framework and built chat feature of the demo app. The Multipeer Connectivity Framework is one of the many new frameworks introduced in iOS 7. As you can see in part 1, the framework allows developers to easily establish communication between nearby devices and implement features for data exchanging. In part 2 of the series, let’s continue to explore the Multipeer Connectivity Framework and see how we can implement the file sharing feature.

Enter the multipeer connectivity tutorial.

We’ll continue to work on the demo app. If you haven’t read the first part of the tutorial series, go back and check it out.

multipeer connectivity file sharing

Setup The File Sharing User Interface

Previously, we’ve implemented the chat feature. Now let’s go to implement the second great feature of our sample application, the file sharing. As usual, we’ll start by setting up the interface on Interface Builder, therefore click on the Main.storyboard file to let it appear.

As a first step, go to the Second View Controller scene and select and delete the default contents of it. Then, add the following controls from the Objects Library, setting the same time the properties as described next:

  1. UILabel
    • Frame: X=20, Y=20, Width=280, Height=21
    • Text: My files:
  2. UITableView
    • Frame: X=0, Y=49, Width=320, Height=519

Next, on the table view add a UITableViewCell object, and set its Row Height value to 80. Add the following subviews to it:

  1. UILabel
    • Frame: X=20, Y=8, Width=280, Height=21
    • Tag: 100
  2. UILabel
    • Frame: X=20, Y=37, Width=280, Height=21
    • Tag: 200
    • Color: Light Gray
  3. UIProgressView
    • Frame: X=20, Y=66, Width=280, Height=2
    • Tag: 300

As a final step, select the prototype cell, and in the Attributes Inspector of the Utilities Pane, set the newFileCellIdentifier value to the Identifier field, under the Table View Cell section. Using this identifier we will dequeue the cell later in code.

Here is how the scene should look like after all having added all these controls:

Multipeer Demo - Chat Sample

Now, let’s create an IBOutlet property to connect it to the table view. Open the SecondViewController.h file and add the next declaration:

Also, once being in this file, adopt some required protocols as we’ll need to implement some delegate methods, as shown below:

Go back to the Interface Builder and connect the tblFiles property to the table view, exactly as you did in the previous interface configurations. After having done so, we are ready to proceed and write some code.

Sharing Files

We’ll begin the implementation on the SecondViewController class just like we did in the chat feature, meaning that we’ll first declare and instantiate an application delegate object, so we are able to access the mcManager object.

Go to the top of the SecondViewController.m file and import the AppDelegate.h file:

Next, declare an object to the private section of the interface:

Finally, instantiate it in the viewDidLoad method:

For the sake of the sample application, two demo files are provided to be used for sharing from peer to peer, the sample_file1.txt and the sample_file2.txt. It’s a good time now to download them and add them to the project.

These two files, along with any files that will be transfered from other peers, should reside in the Documents directory of the application. Also, before we begin sending and receiving any file, we need to display them on our table view.

So, let’s start working on all this, and our first task will be to take the sample files from the application bundle and copy them to the documents directory. We’ll do that by using a private method that we’ll create right next, but first, let’s declare it in the private section of the interface. Apart from the method declaration, we will also declare a NSString object to keep the documents directory path:

Let’s go to the implementation now:

First of all, we specify and keep the documents directory path to the documentsDirectory object. This is necessary, as we are going to need it a few times later. Next, the destination path for the sample files is specified and it’s checked if each one of them exists in the documents directory. If any of them is not found, then they are copied from the main bundle to the documents directory, and if any error occurs (hopefully not), we just log its description.

Now we have to call it, and the suitable place to do so is in the viewDidLoad method.

From now on, every time that the view controller is loaded our application will search for those files in the documents directory and will copy them there if they don’t exist.

Regarding the table view, we want it to display all existing files, so we are able to tap on any of them and send them to other peers. That means subsequently that we need to use an array as the datasource for the table view, and this array must contain all files existing on the Documents directory. So, let’s go to declare a NSMutableArray object:

How should we add objects to the arrFiles array? Simply by reading all files from the Documents directory and getting their names. For that reason we will need one more private method that will do exactly that. Let’s declare it too:

Moving straight ahead to its implementation, we are going to use a NSFileManager object to get all contents of the Documents directory as a NSArray object, and then return this array:

There is nothing difficult to this method. If any error occurs, we just show a description to the log. Now, it’s time to add objects to the arrFiles array for first time, and this will take place on the viewDidLoad method.

We are ready to display data on the table view. For starters, let’s make self the delegate and datasource of it, while being in the viewDidLoad:

To let our sample files appear on the table view upon the view controller loading, we need to force it to do that after our arrFiles array have got its values. Therefore add just this line on the viewDidLoad method:

Before you run and test what we’ve done so far in this view controller, you need to implement the table view delegate and datasource required methods. So, here they are:

If you want, run the application to test it, but the only thing you’ll see for now is just the listing of the sample files.

Let’s keep going to enable the application send a file once it gets selected. What we actually want to happen, is when tapping on a table view row, a list of all peers to appear so we choose the peer that the selected file should be sent to. To make things easy for us and for the purposes of this example, we will use a UIActionSheet object, where each button of it will represent a peer. We want the action sheet to appear every time we tap on a row, so let’s see how this is done:

We start the implementation of the tableview:didSelectRowAtIndexPath: method by creating an action sheet object, providing the selected file name as its title. It might look weird that we set the nil value to all button titles, but this is done in purpose. It would be really handful to us if we could create a nil-terminated string that would contain all peer display names and set them as button titles, but unfortunately there is no way to do that. Therefore there is the for loop, where we add each single peer name one by one as a button to the action sheet. Right after the loop, we set the cancel button, which is necessary in case we just want to close the action sheet, and finally we display it.

Using the last two lines we keep the selected file name and the selected row in two private members, as we will need to know these two values later. Xcode throws an error though, as we haven’t still declared them, so let’s do it now:

Now every time that a file is selected on the table view, an action sheet appears, containing every peer name as a button so we can choose the file’s recipient. But what is going to happen after we have a peer selected on the action sheet? Well, nothing, as we haven’t implemented any behaviour yet.

We need to implement the actionSheet:clickedButtonAtIndex: delegate method (that’s also why we adopted the UIActionSheetDelegate protocol previously). In there, after having checked that a peer name has been tapped and not the cancel button, we will invoke another new to us method of the Multipeer Connectivity framework, named sendResourceAtURL:withName:toPeer:withCompletionHandler:. Actually, this is a method of the MCSession class. In the completion handler we will check if any error has occurred during the sending process, or if the file has successfully sent. In any case, we will let us know about the result.

An important thing necessary to be denoted here is the fact that this method returns a NSProgress object. NSProgress is a new class in iOS 7, so pay a visit to the Apple’s documentation if you’d like to know more about it. Anyway, we care about the results of the method, as this is our only way to keep track of the sending progress and update our UI by showing a percentage value of the completion of the whole process. However, there is a big trap here and that is that our interface will freeze if we call this method on the main thread. Don’t worry though, as this is an easy obstacle to overcome. We will simply do our entire job inside a dispatch_async block, so our interface will remain responsive while sending files.

Just a last note before we see its implementation. As this is a demo application, we want to know what files come from other peers, so we make sure that our code really works. Therefore, as you’ll see in the implementation right next, we slightly modify the file name, by adding the peer’s display name.

The implementation:

Let me point a few things out regarding this code fragment:

  1. Any error description is just logged, but in the case of a successful sending we show an alert view to the user. Note that this code runs on a secondary queue, so we show the alert view on the main thread of the application.
  2. You’ll probably wonder what those two lines are, just right after the alert view:

    Well, as you will finally find out by yourself and as I have already stated, a percentage value is going to be displayed next to the selected file name during the sending process, indicating the whole progress. After a sending has been completed, we need to simply show the file name again, so that’s what we do here. We just replace the combined file name and progress value with the single file name, by updating the array and the table view subsequently. Further than that, here it’s clearly shown why we kept the selected row and file name on the selectedRow and selectedFile members respectively.

  3. Where do we keep track of the progress? Nowhere yet, as it’s necessary to say that the NSProgress class contains a property named fractionCompleted that gives us the progress as a double value. Moreover, all NSProgress class properties, including the fractionCompleted, are KVO (Key-Value Observing), so we must observe for any value changes of this property and update appropriately our UI.

So, having said all that, it’s clear that our next goal is to observe the fractionCompleted value of the progress. To do that, add the next code fragment right before the dispatch_async block closing:

With this way, we register our class with the progress object, so we can observe any changes on it.

Now, implement the next one, so we are notified about all value changes of the fractionCompleted property.

As you see, we create a new string value that contains both the selected file name and the current progress expressed as a percentage value. This string replaces the existing object at the specific index in the array, and the table view is updated on the main thread, so it reflects the sending progress.

If you feel so, go and give it a try. Send files and watch it working. The recipient won’t receive anything yet, as for the time being we have implemented the sender’s side, but not the receiver’s.

Multipeer Demo - Sending File

Let’s focus now on the actions that should be taken when a file is received. In this case, we are going to use the table view cell prototype we previously added in the Interface Builder. While a file receiving is in progress, we will display in the last row of the table view such a cell, showing the file’s name, the sender and the progress using the progress view object. When a file has been successfully received, this cell will be replaced by a default, normal cell that will display just the file name, exactly as the sample files are shown.

Open the MCManager.m file, and locate the session:didStartReceivingResourceWithName:fromPeer:withProgress: delegate method. Its name clearly states its purpose, and we will use it to keep track of the progress while a new file is being received. In here we will act just like we did at the previous two session delegate methods, simply by adding the parameter values into a NSDictionary object and posting a new notification.

There is something more we’ll do here though. As we want to watch the progress of the file being received and update our progress view accordingly, we will register the class with the NSProgress object of the parameter, so we can observe any changes taking place on the fractionCompleted property. Actually, we are going to do the exact same thing we previously did on the file sending feature implementation, where we also added code for observing the fractionCompleted changes. So, here it is:

As you see, we also use a dispatch_async block to observe the progress.

Now, implement the next method so we are notified on any progress changing:

Every time that the progress is changed, we will post a new notification.

Let’s head back to the SecondViewController.m file now, and let’s deal with these two new notifications. In the viewDidLoad method add the next code snippet:

The didStartReceivingResourceWithNotification: and the updateReceivingProgressWithNotification: are both private methods that we will implement right next. Let’s begin by declaring them in the private section of the interface:

Let’s go with the first one:

What we do here is pretty clear: We add the user info dictionary of the notification as an object to the arrFiles array, and we reload the table view data so the new file name, the sender and the initial value of the project to be displayed. But are they really going to be displayed? The answer is not until we update the tableView:cellForRowAtIndexPath: method of the table view, as it is currently able to show just string values in the default cell type. Let’s see the code first:

Here is the deal: Our strategy is to check the kind of the class for each object existing in the arrFiles array. If it’s a NSString value, then the code we already have written is executed. However, if the object of the array that is about to be processed is kind of NSDictionary class, then we dequeue the prototype cell using the identifier we set in the Interface Builder, and then based on the tag values of each subview added to it, we set the dictionary values, after having them extracted from the dictionary object.

Besides this method, alter the following as well:

That’s necessary so the last row to have the appropriate height when a file is being received and everything to be properly displayed.

If you run the app now, you’ll see that when a file is starting to be received, a new row appears on the table view, but you’ll notice that there’s no progress at all. That’s because we haven’t implemented the updateReceivingProgressWithNotification: private method yet, and now it’s the right time to do it.

What’s actually taking place here is quite simple. From the current dictionary existing in the arrFiles array, we get the file name and the peer display name, and using the updated progress we add all these three objects to a new dictionary. Then we replace the previous dictionary existing in the last index of the array with the new one, and lastly we reload the table view.

If you run it now, you’ll see that when a file is being received the progress view perfectly shows the progress.

One last thing remains to be done, and that is to save the file in the Documents directory once it’s received. For the last time in this tutorial, let’s open the MCManager.m file and let’s go now to the session:didFinishReceivingResourceWithName:fromPeer:atURL:withError: delegate method. This one is invoked when a resource has been received, and as usual, we’ll post a new notification.

Let’s move back to the SecondViewController.m file, and let’s make our class able to observe for this notification too inside the viewDidLoad method:

Following the same steps that many times followed until now, let’s declare the didFinishReceivingResourceWithNotification: method:

As you’ll see in the implementation, we copy the file (the resource in general) from the URL temporarily been stored to the Documents directory. At the end of the method, we remove all objects from the arrFiles array, and we re-read the entire Documents directory contents. There is nothing special that should be discussed any further, so here it is:

The file sharing feature has been now completed, and that also means that our demo application is totally ready!

Compile And Run The App

I’d be surprised if you haven’t run the application yet. However, if that’s your case, then now it’s the best and appropriate time to do it. Try it using a couple of devices, or a device and the iPhone Simulator. Make connections, send text messages and share files.e

Multipeer Demo - Receiving File

Summary

The Multipeer Connectivity framework is a brand-new feature on iOS 7. In this tutorial series, we walked through only from a few of its potentialities, as they don’t stop here. There are a lot of things one could still explore, such as how to take security issues under account. Further than that, possibilities for new kind of applications are now available, with just one limitation, your imagination. This framework offers nice tools, and how they’ll be used it’s up to every developer’s desire. Through this tutorial I tried to offer an intro point to all those who would like to deal with Multipeer Connectivity, and I hope you find it really useful. Happy Multipeer Connections!

For your complete reference, you can download the Xcode project of the demo app from here. Feel free to leave me feedback and comment below.

Tutorial
A Beginner’s Guide to Auto Layout with Xcode 10
iOS
How to Build a Form UI with SwiftUI
Tutorial
Using Vision Framework for Text Detection in iOS 11
  • Hugh Mbaezue

    Gabriel you are awesome! Is there a way you can send actual images taking with your iOS device as a file?


    • Gabriel Thedoropoulos

      Thanks a lot!

      Yes, you can do it; here are the steps in a list format:

      1. Capture the image you want with the camera of your device.

      2. Save it as a .png or .jpg file on the documents directory.

      3. Use this file and the Multipeer Connectivity to send it to other nearby devices.

      If you are not sure how to perform the first two steps, just make a search on the web and you’ll find all you need there.

      Good luck!


  • Alicia V Carr

    Thank Gabriel this is like the BEST!!! you are AWESOME!!! I have some questions is there a way that I can connect to a person and no one else or a location also can a camera be added.


    • Gabriel Thedoropoulos

      Hi,

      Firstly, thank you very much!

      Surely, you can establish a connection between two peers only, as long as none of them will invite other people to the session. Also, before a connection gets established, a confirmation alert view appears which you can use to decline other invitations made to you.

      I’m not quite sure what exactly you mean with the location, so please explain more if you’d like.

      Lastly, I guess that when you’re saying about adding a camera, you mean to implement live chat. Well, what you actually want to do here is to perform streaming between the peers, and that’s the third purpose of the multipeer connectivity framework, and also the one that was not covered in any of these two tutorials. If you manage to implement streaming, then combining it with the multipeer connectivity wouldn’t be hard. Anyway, the final answer is yes, you can have live cam functionality.


      • Alicia V Carr

        Thank for the fast reply what I mean about location is connecting up to like a shelter for teens


        • Gabriel Theodoropoulos

          Yes, sure. Once all devices (peers) share the same WiFi network and programmatically the same service type, you can use multipeer connectivity the way you want.


  • nithin bhaktha

    Wow Gabriel, this post is just amazing. Firstly, thanks for the wonderful introduction to a very special framework. But I have a query here. Something’s not working for me ! And I don’t have much experience so I just thought I will clear it this way.

    Well, the thing is, I created a network on my Macbook Pro and I had 2 iOS 7 (iPad 2 & iPod Touch) devices join to this network. When I started Browse for devices, I just have the “Searching ” and the wheel spinning. Apparently one device is not discovering the other device. But the setup works when I have bluetooth switched ON on both. Please, can you help me with this ?


    • Gabriel Theodoropoulos

      Hi,
      Firstly, thanks a lot!

      In your problem now, what exactly do you mean when saying you created a network on your Macbook? Normally, simply by having your 2 iOS devices sharing the same WiFi network you should be able to discover them. Try to delete the demo app from your devices and re-install them. Also, make sure that all IBOutlet properties have been connected to the appropriate controls, in the ConnectionsViewController class. Finally, make sure that your WiFi works fine and without problems.

      I hope something from all these work for you.

      Gabriel


      • nithin bhaktha

        Hey,

        Thanks for the quick response. Well, to connect to a same WiFi network, I created a network under WiFi preference Just below the list of WiFi’s, as I dint wanted internet connectivity. If not in this way, please suggest me the alternate.

        Thanks,
        Nithin


        • Gabriel Theodoropoulos

          Hi,
          Make sure that you have only one WiFi service enabled, and don’t add an extra service (such as WiFi 2) just to test the app. This is probably your problem. I’ve tested it in a couple of different WiFi networks and I had no problem at all. If that’s the case, there must be a conflict to your network that should be resolved first.


          • nithin bhaktha

            Thanks a lot Gabriel, I will surely make a note of this!


          • Gabriel Theodoropoulos

            Well, you might have a point, but I can’t try it out as I don’t have an iPad 2. Maybe another reader who owns one could tell us after some testing. Let’s keep in mind that not every iOS 7 feature may be available on iPad 2, just like it happens in iPhone 4. Anyway, good thought!


  • EW Parris

    EW ParrisEW Parris

    Author Reply

    This is a great walkthrough. Thanks.


  • Gleb Ustimenko

    Hi! Thanks for tutorial! I have a small question – how to correct reset all services after enter background? I am using MCNearbyServiceAdvertiser and MCNearbyServiceBrowser. After app did become active browser find peer, send invitation, receive invitation, but not connected. Nothing happens after peer accept invitation.


    • Gabriel Theodoropoulos

      Hi Gleb,
      You must re-initialize all related objects. Multipeer Connectivity does not work on background.


  • Ben Azoulay

    hi ! thanks for this tutorial !! I have a question how can i read the txt file on the application?


  • Awais Mobeen

    Thanks for the nice tutorial ? have any tutorial on chat app like whats app or nimbuzz ?? could u help me plz??


    • Gabriel Theodoropoulos

      Hi Awais,
      For the time being no tutorial about a chat app is scheduled, but could possibly create one on the future.


      • Hiren Patel

        hi not able to download project with above link


  • Deepak Bhati

    thanks for the awesome tutorial .
    i have only one question that is there any way that send file to all peer like message , yes i can send NSData of image to all peer ,but is it possible send text and image both in same or different message

    please let me know
    thanks


    • Deepak Bhati

      my basic need is send image to all peer just like send text to all ??

      please help


      • Prashant Gaikwad

        Hi Deepak,
        Even I’m trying to implement the same thing.Did you find anything related ?


  • Kalyan

    KalyanKalyan

    Author Reply

    Hi

    Thanks for the awesome tutorial. This is very helpful for understanding the multi-peer frameworks.

    I have a question…please help me out to how can i share the one device internet from this app to other devices. Do we need to have the other devices with the same app installed on them?? if so how can i share the device internet (wifi/3g/2g) from this demo app so that the other device can use the internet from the main device. In this case “other devices” i mean either IOS or Android devices or both.

    Please help me out. Thank you.


  • Babysmily Dronavalli

    Hi,
    Thanks for awesome material.
    I am new to this frame work.can you tell me how to check this project in devices.
    Please help me


  • Jonathon Bolitho

    Hi Gabriel,
    I have a question, could you possibly do a tutorial on connecting between peers without going through the MCBrowserView? For example, two anonymous users connecting automatically and transferring data (maybe using a popup stating – connect with someone: accept/decline).
    The MCBrowserView is just a bit restrictive if it’s not completely necessary to select a particular user. Thank you and keep up the great info!


  • Michael Kiefte

    Hi Gabriel,
    Thanks very much for the tutorial. The Apple documentation on the MC framework is kind of sparse.
    I’m having trouble with your app when one device disconnects from the session. If you try to reconnect, you get odd behaviour. Sometimes it will work for a bit, then one device loses the connection while the other device believes it’s still connected. Other times it just won’t connect at all.
    I tried setting up my own connection and got the same thing.
    So if you try disconnecting one device, all is fine, but if you try to reconnect and use the app, it doesn’t quite work.
    Is this known? Is there a way to work around it?


  • ebrahim Elgeded

    is there is away to use this over internet not nearby devices


  • Gray

    GrayGray

    Author Reply

    Awesome tutorial! Are you planning on adding streaming? Because I have been trying to stream mic audio from one device to another and I can’t figure it out for the life of me. I have the audio being processed using the the audio toolbox and it can listen and send the audio to the top speaker but I can’t figure out how to stream it to a connected user… arrrr. Any help would be appreciated. thanks


  • Abner Abbey

    Why do you say you are doing the NSProgress jobs on a secondary thread? I see you are doing in the main thread. I think it’s much better to make a queue and then call the dispatch_async.

    dispatch_queue_t newQueue = dispatch_queue_create(“ProgressQueue”, NULL);
    dipatch_async(newQueue, ^{
    //Perform nsprogress operations
    dispatch_async(dispatch_get_main_queue( ), ^{
    //Update the UI
    });
    });


  • Prashant Gaikwad

    Hi Gabriel,
    Thank you so much for the tutorial

    Do you have same project in swift ?


  • Himanshu Jamnani

    not able to download project with above link


  • Jorge

    JorgeJorge

    Author Reply

    Unable to download the source code.


  • Tom Saxton

    Tom SaxtonTom Saxton

    Author Reply

    When I try to download the app, I get a Dropbox page that says “file not found.”


  • Hiren Patel

    Link is expired how can i download it?


  • Hiren Patel

    not able to download project with above link


  • Mohammad Shraim

    download links not working!!


Shares