This series shows how to create the web service backend for an iOS app I created called iGlobe. Here’s what we’ve accomplished so far:

 

In this final installment of our series, we put all of the pieces together and show how to exchange data between devices.

Note: I had to change course from my original plan of featuring the Bump API in part five of this series, but it was announced that support for Bump ended Jan. 31, 2014.

P2P

We’ll use iOS 7’s Multipeer Connectivity Framework to
communicate between two devices. We’ll only be exchanging string data for now, but you can exchange many
other data formats.

We have our user’s list in a tableview. Users must be able to post data to the
tag table. This is where the Tag
Class comes in. Create a new
NSObject subclassed file and name it Tag. Now replace its code with the following:

 

#import <Foundation/Foundation.h>

@interface Tag : NSObject {
NSNumber *rglatitude;
NSNumber *rglongitude;
NSString *originUdid;
NSString *destintyUdid;
NSString *rgcountry;
NSString *sender;
NSString *receiver;

}
@property(nonatomic,copy)NSString *destintyUdid;
@property(nonatomic,copy)NSNumber *rglatitude;
@property(nonatomic,copy)NSNumber *rglongitude;
@property(nonatomic,copy)NSString *originUdid;
@property(nonatomic,copy)NSString *rgcountry;
@property(nonatomic,copy)NSString *sender;
@property(nonatomic,copy)NSString *receiver;

-(id)initWithOriginUdid:(NSString*)oudid
rglatitude:(NSNumber*)lati
rglongitude:(NSNumber*)longi;

-(id)initWithSender:(NSString*)senderi
receiver:(NSString*)receiveri
rglatitude:(NSNumber*)lati
rglongitude:(NSNumber*)longi
rgcountry:(NSString*)rgcountri;

-(id)initWithOriginUdid:(NSString*)oudid
destintyUdid:(NSString*)dudid
rglatitude:(NSNumber*)lati
rglongitude:(NSNumber*)longi
rgcountry:(NSString*)rgcountri;

@end

#import "Tag.h"

@implementation Tag

-(id)initWithOriginUdid:(NSString*)oudid
rglatitude:(NSNumber*)lati
rglongitude:(NSNumber*)longi
{
NSLog(@"TAG INIT");
if ( (self = [super init]) == nil )
return nil;

self.rglatitude = lati;
self.rglongitude = longi;
self.originUdid = oudid;
return self;
}

-(id)initWithSender:(NSString*)senderi
receiver:(NSString*)receiveri
rglatitude:(NSNumber*)lati
rglongitude:(NSNumber*)longi
rgcountry:(NSString*)rgcountri
{
NSLog(@"TAG INIT OF TYPE SENDER RECEIVER");
if ( (self = [super init]) == nil )
return nil;

self.receiver = receiveri;
self.rglatitude = lati;
self.rglongitude = longi;
self.sender = senderi;
self.rgcountry = rgcountri;
return self;
}

-(id)initWithOriginUdid:(NSString*)oudid
destintyUdid:(NSString*)dudid
rglatitude:(NSNumber*)lati
rglongitude:(NSNumber*)longi
rgcountry:(NSString*)rgcountri
{
NSLog(@"TAG INIT");
if ( (self = [super init]) == nil )
return nil;

self.destintyUdid = dudid;
self.rglatitude = lati;
self.rglongitude = longi;
self.originUdid = oudid;
self.rgcountry = rgcountri;
return self;
}

@end

Now we have a class that creates Tag objects, and we have
given it different initializers. Every time a user wants to create a tag, they will have to use one of
these methods. Users will create
tags by bumping phones, and each time users bump their phones they will be able to exchange tags. This is where the Multipeer Connectivity Framework comes in.

The grand scheme will be:

  1. Add the Multipeer Connectivity Framework to our
    project.
  2. Add code to create a connection.
  3. Add the code to exchange data.

The first step is the easiest–select the Blue Project folder atop your File
Navigator on the leftmost pane. From the tabs that appear on the Main Editor Window, select Build
Phases. Click the + button, type Multipeer, and select the Multipeer Framework. This will add the necessary files to
your project.

For step 2, in your AppDelegate.h add the following lines:

 

#import <MultipeerConnectivity/MultipeerConnectivity.h>
extern NSString *const kServiceType;
@property (strong, nonatomic) MCSession *session;
@property (strong, nonatomic) MCPeerID *peerId;
extern NSString *const DataReceivedNotification;
– (void)sendDataToPeer;

We’re importing the proper framework and declaring a
couple of constant strings, a few properties, and the method that will be called
to send the data to the other user. The two properties are required to make the connection: a session
property and a peerID property.

In your AppDelegate.m add the following lines:

 

NSString *const kServiceType = @"datashare";
@property (strong, nonatomic) MCAdvertiserAssistant *advertiserAssistant;
NSString *const DataReceivedNotification = @"com.santiapps.iGlobe:DataReceivedNotification";

We’ll use a string identifier for the service type,
an advertiser assistant to advertise that we are running Multipeer Connectivity, and an NSNotification identifier to call when data has been
exchanged successfully. Add this protocol to the interface declaration:

 

<MCSessionDelegate>

Now that we have a way to connect, let’s work on the code
for actually doing so and exchanging the data we need to exchange.

Our AppDelegate will manage the
session, and our MapViewController will initiate and end the data exchange. In order for the MapViewController to
do those things, it needs to talk to the AppDelegate, which will manage the
connection.

In the AppDelegate’s appDidFinishLaunchingWithOptions
method add the following lines:

 

// 1
NSString *peerName = [[UIDevice currentDevice] name];
self.peerId = [[MCPeerID alloc] initWithDisplayName:peerName];
// 2

self.session =[[MCSession alloc] initWithPeer:self.peerId securityIdentity:nil encryptionPreference:MCEncryptionNone];
self.session.delegate = self;
// 3
self.advertiserAssistant =
[[MCAdvertiserAssistant alloc] initWithServiceType:kServiceType discoveryInfo:nil session:self.session];

// 4
[self.advertiserAssistant start];

Here we are assigning a unique name to peerName, initting
one of the peers to be used with that peerName, and initting a session with that
peer. We set the AppDelegate to be
the delegate to the peer connection object.

Finally, we create our advertiser assistant, which will
advertise our availability when needed to other devices, and we start it so we
can be discoverable.

The AppDelegate will be notified when important protocol
events occur. Let’s add the
methods required by this delegate protocol.

Just below the appDidFinishLaunchingWithOptions method add
the following methods:

 

# pragma mark – Add bump-p2p
-(void)session:(MCSession *)session didReceiveData:(NSData *)data fromPeer:(MCPeerID *)peerID{
NSString *gotUser = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"gotUser: %@", gotUser);
[[NSNotificationCenter defaultCenter] postNotificationName:DataReceivedNotification object:nil];
}

This method receives the data
exchanged. Once we get it, we
decode it–in this case because we are simply getting an NSString that was UTF8
encoded. After it is received, we
post a notification to the center reporting that we received it.

 

-(void)session:(MCSession *)session didReceiveStream:(NSInputStream *)stream withName:(NSString *)streamName fromPeer:(MCPeerID *)peerID{
}

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

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

-(void)session:(MCSession *)session peer:(MCPeerID *)peerID didChangeState:(MCSessionState)state {
}

These delegate methods are used for advanced
features that we don’t use in this tutorial. The methods are included here so you can see the many features
available to developers when receiving data.

 

-(void)sendDataToPeer {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:@"storedUser"]) {
NSData *myUser = [[defaults objectForKey:@"storedUser"] dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
[self.session sendData:myUser toPeers:[self.session connectedPeers] withMode:MCSessionSendDataReliable error:&error];
} else {
NSLog(@"ERROR");
}
}

In the end, the sendDataToPeer method takes the storedUser
object from NSUserDefaults, encodes it into NSData, and sends it to the other
peer.

So who calls these methods? The class we want to invoke these methods is the one the
user will be interacting with when he or she decides to share the data with a
user.

In our MapViewController class add the following lines:

 

#import <MultipeerConnectivity/MultipeerConnectivity.h>
<MCBrowserViewControllerDelegate>

We import the Multipeer Connectivity Framework
and adopt the protocol.

Now we add the methods that will be called, perhaps at the
touch of a button:

 

#pragma bump-p2p
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self dataReceived:nil];
}

-(void)browserViewControllerWasCancelled:(MCBrowserViewController *)browserViewController{
[browserViewController dismissViewControllerAnimated:YES completion:nil];
}

-(void)browserViewControllerDidFinish:(MCBrowserViewController*)browserViewController{
[browserViewController dismissViewControllerAnimated:YES completion:^{
[self sendData];
[self showMessage:@"didFinish"];
}];
}

We set up delegate methods of what will
happen when the browserViewController is cancelled or dismissed. The BrowserViewController will pop up
when the user taps for a connection and display the peers available for connectivity. It also requires a delegate, which will
be the MapViewController.
This should be familiar from working with Cancel and Done buttons.

 

-(void)sendData {
AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate sendDataToPeer];
}

To send data we need to call our delegate’s
sendDataToPeer method:

 

-(void)showMessage:(NSString*)message{
[[[UIAlertView alloc] initWithTitle:@"DEBUG" message:message delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
}

Keep the user notified of what is going on by using a simple UIAlertView to present the user with information:

 

-(IBAction)sendButtonPressed:(id)sender {
AppDelegate *delegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
if ([[delegate.session connectedPeers] count] == 0) {
MCBrowserViewController *browserViewController = [[MCBrowserViewController alloc] initWithServiceType:kServiceType session:delegate.session];
browserViewController.delegate = self;
[self showMessage:@"No Peers"];
[self presentViewController:browserViewController animated:YES completion:nil];
} else {
[self sendData];
[self showMessage:@"Peers: Sending Data"];
}
}

This is the main method in MapViewController
regarding peer connectivity. It
uses the AppDelegate to check if there are any peers available. If there are peers available, it creates a
MCBrowserViewController in order to display them to the user. If there are no peers available, it presents
the correct UIAlertView; otherwise, it shows a different message and begins
sending data.

 

-(void)dataReceived:(NSNotification *)notification{
[self showMessage:@"dataReceived called"];
}

The dataReceived method is called when the data is
received, and it shows the appropriate method.

We must take care of some NSNotification setup and clean
up. In viewDidLoad:

 

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(dataReceived:) name:DataReceivedNotification object:nil];
And in viewDidUnload we unregister:

 

[[NSNotificationCenter defaultCenter] removeObserver:self];

Run this on your device, and use a
friend’s device to exchange data between phones or tablets.

Need help?

We
hope you enjoyed this long multi-part tutorial. Drop
us a line if you need help with the steps described in this series.