0
点赞
收藏
分享

微信扫一扫

Understanding Multipeer Connectivity Framework in iOS 7 – Part 2

东言肆语 2023-10-07 阅读 19


http://www.appcoda.com/intro-ios-multipeer-connectivity-programming/

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.




Understanding Multipeer Connectivity Framework in iOS 7 – Part 2_ios


Setup The File Sharing User Interface

Main.storyboard

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:

UILabel   
Frame: X=20, Y=20, Width=280, Height=21
Text: My files:
UITableView   
Frame: X=0, Y=49, Width=320, Height=519
UITableViewCell object, and set its Row Height
UILabel   
Frame: X=20, Y=8, Width=280, Height=21
Tag: 100
UILabel   
Frame: X=20, Y=37, Width=280, Height=21
Tag: 200
Color: Light Gray
UIProgressView   
Frame: X=20, Y=66, Width=280, Height=2
Tag: 300

Attributes Inspector of the Utilities Pane, set the newFileCellIdentifier value to the Identifier field, under theTable View Cell

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



Understanding Multipeer Connectivity Framework in iOS 7 – Part 2_Go_02


SecondViewController.h

@interface          SecondViewController          : UIViewController          

@property                   (         weak         ,                   nonatomic         )                   IBOutlet                   UITableView                   *tblFiles         ;

@end


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

@interface                   SecondViewController                   : UIViewController                   <         UITableViewDelegate         ,                   UITableViewDataSource         ,                   UIActionSheetDelegate         >



tblFiles

Sharing Files

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

SecondViewController.m file and import the AppDelegate.h


#import "AppDelegate.h"


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


@interface                   SecondViewController                   (         )

@property                   (         nonatomic         ,                   strong         )                   AppDelegate                   *appDelegate         ;

@end


viewDidLoad


-                   (         void         )         viewDidLoad

{

             [         super                   viewDidLoad         ]         ;

    

             _appDelegate                   =                   (         AppDelegate                   *         )         [         [         UIApplication                   sharedApplication         ]          delegate         ]         ;

}


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

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


@interface                   SecondViewController                   (         )

.         .         .

                  

@property                   (         nonatomic         ,                   strong         )                   NSString                   *documentsDirectory         ;

                  

-         (         void         )         copySampleFilesToDocDirIfNeeded         ;

                  

@end


Let’s go to the implementation now:


-         (         void         )         copySampleFilesToDocDirIfNeeded         {

             NSArray                   *paths                   =                   NSSearchPathForDirectoriesInDomains         (         NSDocumentDirectory         ,                   NSUserDomainMask         ,                   YES         )         ;

             _documentsDirectory                   =                   [         [         NSString                   alloc         ]          initWithString         :         [         paths          objectAtIndex         :         0         ]         ]         ;

    

             NSString                   *file1Path                   =                   [         _documentsDirectory          stringByAppendingPathComponent         :         @"sample_file1.txt"         ]         ;

             NSString                   *file2Path                   =                   [         _documentsDirectory          stringByAppendingPathComponent         :         @"sample_file2.txt"         ]         ;

    

             NSFileManager                   *fileManager                   =                   [         NSFileManager                   defaultManager         ]         ;

             NSError                   *error         ;

    

    

             if                   (         !         [         fileManager          fileExistsAtPath         :file1Path         ]                   ||                   !         [         fileManager          fileExistsAtPath         :file2Path         ]         )                   {

                 [         fileManager          copyItemAtPath         :         [         [         NSBundle                   mainBundle         ]          pathForResource         :         @"sample_file1"          ofType         :         @"txt"         ]

                             toPath         :file1Path

                              error         :         &         error         ]         ;

        

                 if                   (         error         )                   {

                     NSLog         (         @"%@"         ,                   [         error          localizedDescription         ]         )         ;

                     return         ;

                 }

        

                 [         fileManager          copyItemAtPath         :         [         [         NSBundle                   mainBundle         ]          pathForResource         :         @"sample_file2"          ofType         :         @"txt"         ]

                             toPath         :file2Path

                              error         :         &         error         ]         ;

        

                 if                   (         error         )                   {

                     NSLog         (         @"%@"         ,                   [         error          localizedDescription         ]         )         ;

                     return         ;

                 }

             }

}


First of all, we specify and keep the documents directory path to thedocumentsDirectory

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


-                   (         void         )         viewDidLoad

{

             .         .         .             

             [         self                   copySampleFilesToDocDirIfNeeded         ]         ;

}


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.

NSMutableArray

@interface                   SecondViewController                   (         )

.         .         .

                  

@property                   (         nonatomic         ,                   strong         )                   NSMutableArray                   *arrFiles         ;

@end



How should we add objects to the arrFiles

@interface                   SecondViewController                   (         )

.         .         .

                  

-         (         NSArray                   *         )         getAllDocDirFiles         ;

                  

@end



NSFileManagerobject to get all contents of the Documents directory as a NSArray


-         (         NSArray                   *         )         getAllDocDirFiles         {

             NSFileManager                   *fileManager                   =                   [         NSFileManager                   defaultManager         ]         ;

             NSError                   *error         ;

             NSArray                   *allFiles                   =                   [         fileManager          contentsOfDirectoryAtPath         :         _documentsDirectory          error         :         &         error         ]         ;

    

             if                   (         error         )                   {

                 NSLog         (         @"%@"         ,                   [         error          localizedDescription         ]         )         ;

                 return                   nil         ;

             }

    

             return                   allFiles         ;

}


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

-                   (         void         )         viewDidLoad

{

             .         .         .

             _arrFiles                   =                   [         [         NSMutableArray                   alloc         ]          initWithArray         :         [         self                   getAllDocDirFiles         ]         ]         ;

}



self the delegate and datasource of it, while being in the viewDidLoad:

-                   (         void         )         viewDidLoad

{

             .         .         .

             [         _tblFiles          setDelegate         :self         ]         ;

             [         _tblFiles          setDataSource         :self         ]         ;

}



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


-                   (         void         )         viewDidLoad

{

             .         .         .             

             [         _tblFiles          reloadData         ]         ;

}


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:


-         (         NSInteger         )         numberOfSectionsInTableView         :         (         UITableView                   *         )         tableView         {

             return                   1         ;

}

                  

                  

-         (         NSInteger         )         tableView         :         (         UITableView                   *         )         tableView          numberOfRowsInSection         :         (         NSInteger         )         section         {

             return                   [         _arrFiles          count         ]         ;

}

                  

                  

-         (         UITableViewCell                   *         )         tableView         :         (         UITableView                   *         )         tableView          cellForRowAtIndexPath         :         (         NSIndexPath                   *         )         indexPath         {

             UITableViewCell                   *cell         ;

    

             cell                   =                   [         tableView          dequeueReusableCellWithIdentifier         :         @"CellIdentifier"         ]         ;

    

             if                   (         cell                   ==                   nil         )                   {

                 cell                   =                   [         [         UITableViewCell                   alloc         ]          initWithStyle         :UITableViewCellStyleDefault          reuseIdentifier         :         @"CellIdentifier"         ]         ;

                 [         cell          setAccessoryType         :UITableViewCellAccessoryDisclosureIndicator         ]         ;

             }

    

             cell         .         textLabel         .         text                   =                   [         _arrFiles          objectAtIndex         :indexPath         .         row         ]         ;

    

             [         [         cell          textLabel         ]          setFont         :         [         UIFont          systemFontOfSize         :         14.0         ]         ]         ;             

    

             return                   cell         ;

}

                  

                  

-         (         CGFloat         )         tableView         :         (         UITableView                   *         )         tableView          heightForRowAtIndexPath         :         (         NSIndexPath                   *         )         indexPath         {

             return                   60.0         ;

}


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


-         (         void         )         tableView         :         (         UITableView                   *         )         tableView          didSelectRowAtIndexPath         :         (         NSIndexPath                   *         )         indexPath         {

             NSString                   *selectedFile                   =                   [         _arrFiles          objectAtIndex         :indexPath         .         row         ]         ;

             UIActionSheet                   *confirmSending                   =                   [         [         UIActionSheet                   alloc         ]          initWithTitle         :selectedFile

                                                                delegate         :self

                                                       cancelButtonTitle         :nil

                                                  destructiveButtonTitle         :nil

                                                       otherButtonTitles         :nil         ]         ;

    

             for                   (         int                   i         =         0         ;                   i                   <                   [         [         _appDelegate         .         mcManager         .         session          connectedPeers         ]          count         ]         ;                   i         ++         )                   {

                 [         confirmSending          addButtonWithTitle         :         [         [         [         _appDelegate         .         mcManager         .         session          connectedPeers         ]          objectAtIndex         :i         ]          displayName         ]         ]         ;

             }

    

             [         confirmSending          setCancelButtonIndex         :         [         confirmSending          addButtonWithTitle         :         @"Cancel"         ]         ]         ;

    

             [         confirmSending          showInView         :self         .         view         ]         ;

    

             _selectedFile                   =                   [         _arrFiles          objectAtIndex         :indexPath         .         row         ]         ;

             _selectedRow                   =                   indexPath         .         row         ;

}


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

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:


@interface                   SecondViewController                   (         )

.         .         .

@property                   (         nonatomic         ,                   strong         )                   NSString                   *selectedFile         ;

@property                   (         nonatomic         )                   NSInteger                   selectedRow         ;

@end


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

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_asyncblock, 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:

-         (         void         )         actionSheet         :         (         UIActionSheet                   *         )         actionSheet          clickedButtonAtIndex         :         (         NSInteger         )         buttonIndex         {

             if                   (         buttonIndex                   !=                   [         [         _appDelegate         .         mcManager         .         session          connectedPeers         ]          count         ]         )                   {

                 NSString                   *filePath                   =                   [         _documentsDirectory          stringByAppendingPathComponent         :         _selectedFile         ]         ;

                 NSString                   *modifiedName                   =                   [         NSString          stringWithFormat         :         @"%@_%@"         ,                   _appDelegate         .         mcManager         .         peerID         .         displayName         ,                   _selectedFile         ]         ;

                 NSURL                   *resourceURL                   =                   [         NSURL          fileURLWithPath         :filePath         ]         ;

        

                 dispatch_async         (         dispatch_get_main_queue         (         )         ,                   ^         {

                     NSProgress                   *progress                   =                   [         _appDelegate         .         mcManager         .         session          sendResourceAtURL         :resourceURL

                                                                            withName         :modifiedName

                                                                              toPeer         :         [         [         _appDelegate         .         mcManager         .         session          connectedPeers         ]          objectAtIndex         :buttonIndex         ]

                                                               withCompletionHandler         :         ^         (         NSError                   *error         )                   {

                                                                            if                   (         error         )                   {

                                                                                NSLog         (         @"Error: %@"         ,                   [         error          localizedDescription         ]         )         ;

                                                                            }

                                                                   

                                                                            else         {

                                                                                UIAlertView                   *alert                   =                   [         [         UIAlertView                   alloc         ]          initWithTitle         :         @"MCDemo"

                                                                                                                       message         :         @"File was successfully sent."

                                                                                                                      delegate         :self

                                                                                                             cancelButtonTitle         :nil

                                                                                                             otherButtonTitles         :         @"Great!"         ,                   nil         ]         ;

                                                                       

                                                                                [         alert          performSelectorOnMainThread         :         @selector         (         show         )          withObject         :nil          waitUntilDone         :NO         ]         ;

                                                                       

                                                                                [         _arrFiles          replaceObjectAtIndex         :         _selectedRow          withObject         :         _selectedFile         ]         ;

                                                                                [         _tblFiles          performSelectorOnMainThread         :         @selector         (         reloadData         )

                                                                                                   withObject         :nil

                                                                                                waitUntilDone         :NO         ]         ;

                                                                            }

                                                                        }         ]         ;

                             }         )         ;

             }

}



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:

[           _arrFiles            replaceObjectAtIndex           :           _selectedRow            withObject           :           _selectedFile           ]           ;

[           _tblFiles            performSelectorOnMainThread           :           @selector           (           reloadData           )            withObject           :nil            waitUntilDone           :NO           ]           ;


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

  1. Where do we keep track of the progress? Nowhere yet, as it’s necessary to say that the NSProgress class contains a property namedfractionCompleted 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 thefractionCompleted value of the progress. To do that, add the next code fragment right before the dispatch_async


-         (         void         )         actionSheet         :         (         UIActionSheet                   *         )         actionSheet          clickedButtonAtIndex         :         (         NSInteger         )         buttonIndex         {

             if                   (         buttonIndex                   !=                   [         [         _appDelegate         .         mcManager         .         session          connectedPeers         ]          count         ]         )                   {

                 .         .         .

        

                 dispatch_async         (         dispatch_get_main_queue         (         )         ,                   ^         {

                     NSProgress                   *progress                   =                   .         .         .

            

                     [         progress          addObserver         :self

                       forKeyPath         :         @"fractionCompleted"

                          options         :NSKeyValueObservingOptionNew

                          context         :nil         ]         ;

                 }         )         ;

             }

}


progress

fractionCompleted


-         (         void         )         observeValueForKeyPath         :         (         NSString                   *         )         keyPath          ofObject         :         (         id         )         object          change         :         (         NSDictionary                   *         )         change          context         :         (         void                   *         )         context         {

             NSString                   *sendingMessage                   =                   [         NSString          stringWithFormat         :         @"%@ - Sending %.f%%"         ,

                                         _selectedFile         ,

                                         [         (         NSProgress                   *         )         object          fractionCompleted         ]                   *                   100

                                         ]         ;

    

             [         _arrFiles          replaceObjectAtIndex         :         _selectedRow          withObject         :sendingMessage         ]         ;

    

             [         _tblFiles          performSelectorOnMainThread         :         @selector         (         reloadData         )          withObject         :nil          waitUntilDone         :NO         ]         ;

}


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.



Understanding Multipeer Connectivity Framework in iOS 7 – Part 2_ios_03


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 thesession: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

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 fractionCompletedchanges. So, here it is:


-         (         void         )         session         :         (         MCSession                   *         )         session          didStartReceivingResourceWithName         :         (         NSString                   *         )         resourceName          fromPeer         :         (         MCPeerID                   *         )         peerID          withProgress         :         (         NSProgress                   *         )         progress         {

    

             NSDictionary                   *dict                   =                   @         {         @"resourceName"                    :   resourceName         ,

                                    @"peerID"                          :   peerID         ,

                                    @"progress"                        :   progress

                                    }         ;

    

             [         [         NSNotificationCenter                   defaultCenter         ]          postNotificationName         :         @"MCDidStartReceivingResourceNotification"

                                                        object         :nil

                                                      userInfo         :dict         ]         ;

    

    

             dispatch_async         (         dispatch_get_main_queue         (         )         ,                   ^         {

                 [         progress          addObserver         :self

                   forKeyPath         :         @"fractionCompleted"

                      options         :NSKeyValueObservingOptionNew

                      context         :nil         ]         ;

             }         )         ;

}


dispatch_async

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


-         (         void         )         observeValueForKeyPath         :         (         NSString                   *         )         keyPath          ofObject         :         (         id         )         object          change         :         (         NSDictionary                   *         )         change          context         :         (         void                   *         )         context         {

             [         [         NSNotificationCenter                   defaultCenter         ]          postNotificationName         :         @"MCReceivingProgressNotification"

                                                        object         :nil

                                                      userInfo         :         @         {         @"progress"         :                   (         NSProgress                   *         )         object         }         ]         ;

}


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


-                   (         void         )         viewDidLoad

{

             .         .         .

             [         [         NSNotificationCenter                   defaultCenter         ]          addObserver         :self

                                             selector         :         @selector         (         didStartReceivingResourceWithNotification         :         )

                                                 name         :         @"MCDidStartReceivingResourceNotification"

                                               object         :nil         ]         ;

    

             [         [         NSNotificationCenter                   defaultCenter         ]          addObserver         :self

                                             selector         :         @selector         (         updateReceivingProgressWithNotification         :         )

                                                 name         :         @"MCReceivingProgressNotification"

                                               object         :nil         ]         ;

}


didStartReceivingResourceWithNotification: and theupdateReceivingProgressWithNotification:

@interface                   SecondViewController                   (         )

.         .         .

-         (         void         )         didStartReceivingResourceWithNotification         :         (         NSNotification                   *         )         notification         ;

-         (         void         )         updateReceivingProgressWithNotification         :         (         NSNotification                   *         )         notification         ;

@end



Let’s go with the first one:

-         (         void         )         didStartReceivingResourceWithNotification         :         (         NSNotification                   *         )         notification         {

             [         _arrFiles          addObject         :         [         notification          userInfo         ]         ]         ;

             [         _tblFiles          performSelectorOnMainThread         :         @selector         (         reloadData         )          withObject         :nil          waitUntilDone         :NO         ]         ;

}



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 thetableView:cellForRowAtIndexPath:


-         (         UITableViewCell                   *         )         tableView         :         (         UITableView                   *         )         tableView          cellForRowAtIndexPath         :         (         NSIndexPath                   *         )         indexPath         {

             UITableViewCell                   *cell         ;

    

             if                   (         [         [         _arrFiles          objectAtIndex         :indexPath         .         row         ]          isKindOfClass         :         [         NSString                   class         ]         ]         )                   {

                 cell                   =                   [         tableView          dequeueReusableCellWithIdentifier         :         @"CellIdentifier"         ]         ;

        

                 if                   (         cell                   ==                   nil         )                   {

                     cell                   =                   [         [         UITableViewCell                   alloc         ]          initWithStyle         :UITableViewCellStyleDefault          reuseIdentifier         :         @"CellIdentifier"         ]         ;

                     [         cell          setAccessoryType         :UITableViewCellAccessoryDisclosureIndicator         ]         ;

                 }

        

                 cell         .         textLabel         .         text                   =                   [         _arrFiles          objectAtIndex         :indexPath         .         row         ]         ;

        

                 [         [         cell          textLabel         ]          setFont         :         [         UIFont          systemFontOfSize         :         14.0         ]         ]         ;

             }

             else         {

                 cell                   =                   [         tableView          dequeueReusableCellWithIdentifier         :         @"newFileCellIdentifier"         ]         ;

        

                 NSDictionary                   *dict                   =                   [         _arrFiles          objectAtIndex         :indexPath         .         row         ]         ;

                 NSString                   *receivedFilename                   =                   [         dict          objectForKey         :         @"resourceName"         ]         ;

                 NSString                   *peerDisplayName                   =                   [         [         dict          objectForKey         :         @"peerID"         ]          displayName         ]         ;

                 NSProgress                   *progress                   =                   [         dict          objectForKey         :         @"progress"         ]         ;

        

                 [         (         UILabel                   *         )         [         cell          viewWithTag         :         100         ]          setText         :receivedFilename         ]         ;

                 [         (         UILabel                   *         )         [         cell          viewWithTag         :         200         ]          setText         :         [         NSString          stringWithFormat         :         @"from %@"         ,                   peerDisplayName         ]         ]         ;

                 [         (         UIProgressView                   *         )         [         cell          viewWithTag         :         300         ]          setProgress         :progress         .         fractionCompleted         ]         ;

             }

    

             return                   cell         ;

}


Here is the deal: Our strategy is to check the kind of the class for each object existing in the arrFiles

Besides this method, alter the following as well:


-         (         CGFloat         )         tableView         :         (         UITableView                   *         )         tableView          heightForRowAtIndexPath         :         (         NSIndexPath                   *         )         indexPath         {

             if                   (         [         [         _arrFiles          objectAtIndex         :indexPath         .         row         ]          isKindOfClass         :         [         NSString                   class         ]         ]         )                   {

                 return                   60.0         ;

             }

             else         {

                 return                   80.0         ;

             }

}


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

updateReceivingProgressWithNotification:

-         (         void         )         updateReceivingProgressWithNotification         :         (         NSNotification                   *         )         notification         {

             NSProgress                   *progress                   =                   [         [         notification          userInfo         ]          objectForKey         :         @"progress"         ]         ;

    

             NSDictionary                   *dict                   =                   [         _arrFiles          objectAtIndex         :         (         _arrFiles         .         count                   -                   1         )         ]         ;

             NSDictionary                   *updatedDict                   =                   @         {         @"resourceName"                    :                     [         dict          objectForKey         :         @"resourceName"         ]         ,

                                    @"peerID"                          :                     [         dict          objectForKey         :         @"peerID"         ]         ,

                                    @"progress"                        :   progress

                                    }         ;

    

    

    

             [         _arrFiles          replaceObjectAtIndex         :         _arrFiles         .         count                   -                   1

                         withObject         :updatedDict         ]         ;

                  

             [         _tblFiles          performSelectorOnMainThread         :         @selector         (         reloadData         )          withObject         :nil          waitUntilDone         :NO         ]         ;

}



What’s actually taking place here is quite simple. From the current dictionary existing in the arrFiles

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 theMCManager.m file and let’s go now to thesession:didFinishReceivingResourceWithName:fromPeer:atURL:withError:


-         (         void         )         session         :         (         MCSession                   *         )         session          didFinishReceivingResourceWithName         :         (         NSString                   *         )         resourceName          fromPeer         :         (         MCPeerID                   *         )         peerID          atURL         :         (         NSURL                   *         )         localURL          withError         :         (         NSError                   *         )         error         {

    

             NSDictionary                   *dict                   =                   @         {         @"resourceName"                    :   resourceName         ,

                                    @"peerID"                          :   peerID         ,

                                    @"localURL"                        :   localURL

                                    }         ;

    

             [         [         NSNotificationCenter                   defaultCenter         ]          postNotificationName         :         @"didFinishReceivingResourceNotification"

                                                        object         :nil

                                                      userInfo         :dict         ]         ;

    

}


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

{

             .         .         .

             [         [         NSNotificationCenter                   defaultCenter         ]          addObserver         :self

                                             selector         :         @selector         (         didFinishReceivingResourceWithNotification         :         )

                                                 name         :         @"didFinishReceivingResourceNotification"

                                               object         :nil         ]         ;

}



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


@interface                   SecondViewController                   (         )

.         .         .

-         (         void         )         didFinishReceivingResourceWithNotification         :         (         NSNotification                   *         )         notification         ;

@end


arrFiles


-         (         void         )         didFinishReceivingResourceWithNotification         :         (         NSNotification                   *         )         notification         {

             NSDictionary                   *dict                   =                   [         notification          userInfo         ]         ;

    

             NSURL                   *localURL                   =                   [         dict          objectForKey         :         @"localURL"         ]         ;

             NSString                   *resourceName                   =                   [         dict          objectForKey         :         @"resourceName"         ]         ;

    

             NSString                   *destinationPath                   =                   [         _documentsDirectory          stringByAppendingPathComponent         :resourceName         ]         ;

             NSURL                   *destinationURL                   =                   [         NSURL          fileURLWithPath         :destinationPath         ]         ;

    

             NSFileManager                   *fileManager                   =                   [         NSFileManager                   defaultManager         ]         ;

             NSError                   *error         ;

             [         fileManager          copyItemAtURL         :localURL          toURL         :destinationURL          error         :         &         error         ]         ;

    

             if                   (         error         )                   {

                 NSLog         (         @"%@"         ,                   [         error          localizedDescription         ]         )         ;

             }

    

             [         _arrFiles          removeAllObjects         ]         ;

             _arrFiles                   =                   nil         ;

             _arrFiles                   =                   [         [         NSMutableArray                   alloc         ]          initWithArray         :         [         self                   getAllDocDirFiles         ]         ]         ;

    

             [         _tblFiles          performSelectorOnMainThread         :         @selector         (         reloadData         )          withObject         :nil          waitUntilDone         :NO         ]         ;

}


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



Understanding Multipeer Connectivity Framework in iOS 7 – Part 2_ide_04


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.



举报

相关推荐

0 条评论