Good morning and welcome [applause].
Thanks a lot for coming.
So my name is Demijan Klinc and in this session we'll talk about nearby networking with Multipeer Connectivity.
So what is Multipeer Connectivity?
Well, Multipeer Connectivity is a brand new framework we're shipping in iOS 7 that facilitates discovery of and communication with nearby devices.
It abstracts complex programming network programming aspects from you and enables you to build to easily build communication sessions with nearby peers, where they can exchange data with each other.
So essentially, we're looking at a situation like this one, where we have multiple iOS devices in close proximity of each other and what they want to do is they want to establish a communication link and they want to exchange data with each other.
Now this here is a very simple simplistic case and the framework also supports much more complex setups.
Like this one, for instance, where we have many iOS devices that participate in multiple sessions at the same time.
So what are the use cases for this technology?
Well, let me let me name a few.
Interactive tutoring, collaborative document and photo editing, file sharing, coordination across multiple devices, and sensor data aggregation.
Of course, these are just a few examples and the playing field is wide open for you guys to explore.
We can't wait to see what you come up with as you start playing with the framework.
Under the hood, Multipeer Connectivity supports connectivity using classical Bluetooth and infrastructure Wi-Fi.
And, in addition, and we're very excited about this, in iOS 7 we're adding support for peer-to-peer Wi-Fi Networking.
[ Applause ]
So peer-to-peer Wi-Fi Networking will enable your iOS devices to communicate with each other using Wi-Fi technology, even if they're not connected to an access point or if they're connected to different access points.
So we think this will bring nearby networking on iOS to a whole new level.
We're very excited about this.
So what are the features of the framework?
So as I just mentioned, we support multiple wireless technologies.
We have interface selection.
So by that I mean that the framework will choose the best wireless technology for you under the hood, so that you so your users get the best networking experience.
We will give you convenience and convenience, discovery, and invitation UI.
So we'll give you tools that will make it very, very easy for you to discover who is nearby and mechanisms to invite nearby peers and have them connect into a session.
Then, once you're in a session, we will give you the APIs to send data and will support message-based data and stream-based data.
Also, we have built-in support for security as well, and specifically, we'll give you support for authentication and encryption.
Now this brings me to the agenda for today and we'll have two bigger parts.
In the first part, we'll cover the essentials, and in the second part, we'll cover some advanced topics.
In the essentials, we'll look at two phases.
First, we'll look at the discovery phase and the discovery phase will be the phase where you're discovering who's nearby and you're inviting nearby peers to a session, so basically, everything that happens before the peers are successfully connected with each other in a session where they can exchange data.
Then we will cover the session phase and there we'll assume that everybody's already connected and we'll see how you can have them exchange data with each other.
Once we're done with the essentials, we'll look at some advanced topics.
Specifically, we'll look at programatic discovery and we'll look at security.
All right, so let's dive in, the essentials.
First I want to define some terminology that we will use in today's talk and I want to start with nearby.
So what is nearby?
For a particular device, another device will be called nearby if the two devices can communicate directly with each other using one of the supported wireless technologies.
Peer. A peer will be a nearby device.
An advertiser will be a device that makes itself discoverable to other devices.
Similarly, the act of advertising will be the act of making oneself discoverable to other peers.
And lastly, browser.
A browser will be a device that is searching for other nearby devices.
And similarly, the act of browsing will be the act of searching for nearby devices.
Okay. So with that established, let's jump into the discovery phase.
And to start, I want to go with you through the flow of the discovery phase.
So let's assume I'm running an app that uses Multipeer Connectivity and I want to see if there are any peers nearby.
So I bring up my browser, my browser UI that is supplied by the framework, and on my browser UI I can see that I'm searching for nearby peers.
There is currently no one around.
A few moments later, Jeff comes by and I will see that Jeff is nearby on my UI.
Then I will also have Gabe join us and moments later I also see that Gabe is nearby.
So what I want to do now is I want to invite Jeff and Gabe into a session so we can start exchanging data with each other.
How I can do that?
Well I can invite them simply by tapping on their names on my UI.
So let's start with Jeff.
I tap on Jeff and as I do so an invitation will be sent out to Jeff.
Then I will tap on Gabe and an invitation will be sent out to Gabe as well.
When Jeff receives the invitation, we will display an alert for you that notifies Jeff that I want to connect to him.
At this point, Jeff has to decide whether he wants to accept or decline my invitation.
Similar will happen to Gabe.
And let's now assume that both Jeff and Gabe are willing to connect to me.
So they will tap on accept and as they tap on accept an invitation response is sent back to me and we start connecting.
On my UI, when we successfully connect to each other, I will see that I have connected to Jeff and Gabe and the Done button on the in the upper right corner becomes tappable.
So I can press the Done button to dismiss the UI and start exchanging data with Jeff and Gabe.
So that was the flow of the discovery phase.
Let's now see how you guys can make this possible in your apps.
This slide here shows you the type of objects that you will have to use to set up the advertiser and the browser.
So let's start with the advertiser.
What are the tasks of the advertiser?
Well, as we said, the advertiser has to make itself discoverable to other nearby peers.
If an incoming if an invitation comes in, the advertiser has to display that invitation to the user.
And when the user decides whether he wants to accept or decline an invitation, the advertiser has to handle that response.
Also, if the response was positive, it has to hook up the nearby peer into a session.
So these are the tasks of the advertiser.
In code, what you have to do is you have to identify yourself first or identify your device first.
And for that, we give you a class called MCPeerID.
To instantiate that class, you will have to pass it one parameter, an NSString object, which will specify the name that you want to use for your device.
I want to tell I want to note here that the name that you specify here will also be the name that the browser will see on their UI.
So I want to encourage you to keep this name short so we won't have to truncate it in the UI.
Next, you will have to instantiate a session object.
The session object is of class MCSession and it accepts one parameter, which is the MCPeerID Object that we just instantiated earlier.
And also, the MCSession object has a delegate and you'll want to set that delegate to get notified of MCSession-related events.
Lastly, you'll want to use the MCAdvertiserAssistant object and the MCAdvertiserAssistant object is instantiated with three parameters.
The first one is a service type and that's an NSString parameter.
A service type identifies the type of session that the advertiser wants to participate in.
So I want to say that the service type is a Bonjour service type and I encourage you to register it as such once you've decided which service type you want to use.
The second parameter is the discovery info and we'll talk about discovery info in more detail later, so let's just set that to nil for now.
The third parameter and the last parameter is the session object that we instantiated earlier.
Once we've instantiated the advertiser assistant, all we have to do is start it by calling the Start method.
And that's it.
We're done setting up the advertiser.
So let's take a look at the browser now.
So what are the tasks of the browser?
Well, the browser is searching for nearby peers and as it finds nearby peers, it needs to present them to the user for you.
Once a user decides which peer they want to invite, the browser has to send an invitation to that peer.
And when a response comes back, the browser has to handle that invitation and in case the response was positive, it needs to hook up that peer into a session.
So these are the tasks of a browser.
In code, the browser's setup is very similar to the advertiser's setup.
The first two steps are exactly the same.
You need to instantiate an MCPeerID object and you need to instantiate an MCSession object.
Then the last step is to instantiate the MCBrowserViewController object and you have to pass it two parameters.
The first one is a service type and that service type has to be exactly the same as the service type that you specified for the advertiser; namely, a browser will only see those advertisers that have the same service type.
The second parameter is the session object that we just instantiated and which you want the browser view controller to use.
Next, you will have to set the delegate on the browser to get notified of browser-related events.
And when you set the delegate, you're ready to present the browser view controller to the user and as you do so, we will start browsing for you and the rest will be user driven.
[ Silence ]
So at this point, we are presenting the framework is presenting the UI for you and the user is choosing the users to invite and it's waiting for invitation responses to come back.
Once the nearby peers have successfully connected into a session you will see that the or the user will see that the Done button becomes enabled.
And when the user presses the Done button, you will be notified via a delegate method browserViewControllerDidFinish:.
Now inside that method, you will have the opportunity to prepare for the session and you have to dismiss the browser view controller UI.
So that's, again, the overview of classes that you have to use to set up the discovery phase.
And now I want to tell you a bit more about how do you know that the session objects connected with each other and how do they even know who they want to connect to.
Well, let's assume we have a browser view controller on one side and the advertiser assistant on the other side set up and as peers get found and lost, the browser view controller and the advertiser assistant are communicating with each other.
Now the browser's session object will know who the browser wants to connect to when the user taps on someone to invite.
So when an invitation is sent out.
The advertiser's session object will know who the advertiser wants to connect to when the user accepts an invitation.
When that happens, the two session objects will attempt to establish a communication link with each other and once they succeed, you will be notified via a session delegate method peer:didChangeState:.
So let's take a look at this delegate method in a bit more detail.
You can see it here on this slide, session:peer:didChangeState:.
The second parameter will give you the peer ID object of the peer who just changed state and the state will tell you what the new state is.
If the connection was successfully established between the two sessions, the state will by MCSessionStateConnected.
And if something went wrong during the connection process or if the advertiser declined an invitation, the state will be MCSession Not Connected.
So to summarize, the discovery phase is really, really easy to set up.
All you have to do is instantiate an advertiser assistant and start it.
Then you have to instantiate the browser view controller and present it to the user.
And the rest of the process will be entirely user driven.
All you have to do is wait for the session callbacks to tell you when the session has successfully connected to your nearby peers.
Okay, so let's move on to the session phase now.
Now we assume that we have multiple nearby peers connected with each other and they want to exchange data.
So let's take a look at how you guys can do this.
We provide three sets of send data APIs for you: messages, streaming, and resources.
So let's start with messages.
By message I mean a chunk of data, any chunk of data, with well-defined boundaries.
So it's very clear where the message ends, where the message starts, and where the message ends.
So if you send a message in one call, you will also receive a message in one call.
We provide two different modes to send messages for you.
The first one is the reliable mode and you will want to use the reliable mode for application-critical data.
If you send a message in the reliable mode and something happens to the message along the way, for instance, if it gets dropped, the framework will retransmit the message for you.
Also, if you send multiple messages in a row, the framework will make sure that the recipient of the message will get them in order.
Alternatively, you can use the unreliable mode and we encourage you to use the unreliable mode for time-sensitive data.
So if you use the unreliable mode, the framework will make best effort to deliver your messages as soon as possible to the recipient.
So the so with the smallest possible latency.
However, you should know that if something happens to messages that were sent in the unreliable Mode, we will make no attempts to retransmit that message for you.
Also, if you send multiple messages in a row, we give you no guarantees that they will be delivered in order.
So these are the two modes, reliable and unreliable.
Let's take a look at the APIs that you will have to use.
It's really, really simple.
To send a message, you will invoke a method called sendData:toPeers:withMode: and error:.
The first parameter will be where you place you message and that's an NSData object, so you will have to encapsulate your messages into NSData objects.
The second parameter is an NSArray and you will have to pass it an array of peer IDs that designate peers that you want to send a message to.
If you want to if you want to get a handle of all peers that are currently connected in your session, you can do so by invoking connectedPeers method on your session object.
The third parameter will be the mode, which we discussed earlier, either reliable or unreliable.
And the fourth parameter will be an error parameter.
It's an output parameter that the framework will set for you in case something goes wrong when you make the call.
I want to stress here that send data returns immediately.
It's a nonblocking call, so it will return immediately.
It won't wait until the data is delivered or something like that.
On the receiver's side, when the message comes in, the receiver will be notified via a session delegate method, session:did ReceiveData:fromPeer:, and the second parameter will be an NSData object, which will be exactly the same as the one that was specified by the sender.
And the third parameter will be the peer ID of the sender of the message.
So it's that simple to send messages.
Next, let's take a look at streaming APIs.
To start a stream with another peer, you'll have to call the method startStreamWithName:toPeer: and error:.
So in the first parameter you will have to specify a name for the stream that you are trying to open.
And this name is going to be an NSString object.
Second parameter will be the peer ID of the peer you want to send you want to stream data to.
And the third one will be like before, an output error parameter that we will set the framework will set, in case something goes wrong.
Just like the sendData: method, startStreamWithName: is nonblocking and it will return immediately.
As it returns, it will give you an NSOutputStream object that you will be able to use to stream data to the recipient.
On the other side, when the streaming request comes in at the receiver, the receiver is notified via this delegate method, session:did ReceiveStream:WithName: and fromPeer:.
The second parameter will be an NSInputStream object that the recipient can use to receive data.
The third parameter will be the name for the stream that the sender specified.
And the fourth parameter will be the peer ID of the sender.
Once both sender and the recipient have a handle on the stream objects, they'll have to set the delegate on them, they'll have to schedule them in a run loop, and they will have to call open on them.
Of course, they will also have to implement NSStringDelegate methods to send and receive data.
So that was streaming.
And lastly, let's take a look at resource APIs.
So we provide support for sending resources for you and we provide support for sending files and for sending web URLs.
So let's see how you guys can implement that in code.
To send a resource to someone, you will call a method sendResourceAt URL:withName:toPeer: and you will have to pass a completion handler.
The URL will be the URL of the resource that you want to send.
This will be either a file or a web URL.
If you specify a web URL here, the framework will go ahead and fetch the contents of that URL and send it to the recipient.
The second parameter will be an NSString, which will be the name for this resource transfer.
The third parameter will be the peer ID of the recipient.
And the fourth one will be the a completion handler, which the framework will call for you once the resource has been successfully transmitted to the recipient.
Also, the framework will call the completion handler in case something goes wrong.
So in both of these cases, the completion handler will be called.
Like the previous two methods, sendResourceAtURL: is nonblocking and it will return immediately.
As it returns, it will pass to you an NSProgress object.
NSProgress is a new class in Foundation in iOS 7 and it will enable you to query current progress of the transmission.
Also, it will give you the ability to cancel the transmission if you desire to do so.
So let's take a look at what happens on the recipient's side.
When the resource gets when it starts receiving the resource, the receiver's delegate method will be notified via didStartReceivingResource WithName:fromPeer:withProgress:.
The second parameter will be a name that the sender specified.
FromPeer: will be the ID of the sender.
And withProgress: will be an NSProgress object that the recipient can use to monitor current progress or to cancel transmission.
And when the framework finishes transmitting the resource, the receiver's delegate will be notified via didFinishReceivingResource WithName:fromPeer :atURL:withError: method.
Again, the second parameter will be a name.
The third parameter will be the ID of the sender.
The fourth parameter will be the URL where the framework saved the resource locally on the device.
And the last parameter will be an error that the framework will set in case something goes wrong.
So again, this callback will be called in two cases, if the resource has been successfully transmitted or if something went wrong.
In that case, an error parameter will be set for you so that you will know that something went wrong.
So these are the APIs to send data that we provide for you.
We have messages, streaming, and resources.
And with that out of the way, I want to tell you something cool that Multipeer Connectivity provides for you at no cost.
You don't have to do anything to make this functionality possible in your apps.
So let's assume we have three devices, just like on this slide, that want to connect to each other and exchange data with each other [coughing].
Okay, so let's also assume that the device on the left has Bluetooth on and also has Wi-Fi on.
The device on top will have only Bluetooth on, so Wi-Fi's off.
And the device on the right will have Wi-Fi on and no Bluetooth.
So you would think that the device on top and the device on the right cannot communicate with each other because they don't share a common interface.
Bluetooth and Wi-Fi cannot communicate directly with each other, but Multipeer Connectivity has infrastructure built-in that will that will enable these devices to be aware of each other, even if they don't share a common interface and this will be done through the device that has both interfaces available.
[ Applause ]
So we will make sure they're aware of each other and if the device on the right wants to send messages to the device on top we will do do so for you through the device on the left.
So they will be able to seamlessly communicate with each other.
So we think this is really cool.
To disconnect from a session, all you have to do to disconnect yourself from a session all you have to do is call the disconnect method.
And in case someone else disconnects from a session, you will be notified via a session delegate method session:peer:didChangeState:, which we're now familiar with.
The state will be MCSessionStateNotConnected if someone disconnects.
So the session phase summary, we've described three sets of data of send data APIs for you.
We have support for messages and we support reliable and unreliable mode.
We have support for streaming.
And we have support for sending resources.
And that's really it [applause].
So that's all you need to do to make this possible and have multiple nearby peers connect with each other and exchange data with each other.
It's really that easy.
In the discovery phase, all you have to do is instantiate an advertiser assistant and start it.
The on the browser side, you have to instantiate the browser view controller and present it to the user and the rest of the process will be entirely driven by the user and the framework.
Once your peers connect into a session, you can use one of the send data APIs to send messages, stream data, or send resources.
And that wraps up the essentials.
[ Applause ]
So let's move on and let's take a look at some advanced topics.
I want to start with programatic discovery.
So what programatic discovery will enable you to do, it will enable you to handle events like found peer, lost peer, and actions like sending invitations, responding invitations.
It will make it possible for you to handle these things programatically.
So this will give you a lot of flexibility and will enable you to build custom UIs for the discovery phase, if you choose to do so.
So let's take a look at how you can do this.
To set up an advertiser, you will use an MCNearbyServiceAdvertiser object and you will have to pass it three parameters.
The first one will be a peer ID that we're now familiar with.
And the second one will be discovery info that I want to cover in a bit more detail now.
So discovery info is an NSDictionary object that you can set and it's going to be a set of key-value pairs.
This set of key-value pairs will be added to the advertiser's Bonjour TXT record.
That means that when the browser sees this advertiser, it will see this information along with the with the fact that it discovered it.
The third parameter is a service type and we all know what service type now is.
You'll have to set a delegate on the advertiser to get notified of advertiser-related events and to start advertising you will call startAdvertisingPeer method.
On the browser side, you will need an MCNearbyServiceBrowser object and to instantiate it you will need to pass it two parameters.
First one will be the peer ID and the second one will be a service type, which again, I would like to emphasize that the two service types, the one that the advertiser specifies and the one that the browser specifies they need to be the same, otherwise they won't see each other.
Also, you will need to set the delegate on the browser and to start browsing, you will have to call startBrowsingForPeers method.
So let's now go through the flow if you choose to do programatic discovery.
We have three devices here.
The device on the left will be the browser.
The device in the middle and the device on the right will be advertisers.
When the browser discovers an advertiser, the browser's delegate method will be notified via a foundPeer:withDiscoveryInfo: callback.
This same callback will be called every time an advertiser is discovered.
So let's take a look at how it looks.
This is the callback browser:found Peer:withDiscoveryInfo:.
The second parameter will give you the peer ID object of the peer that just got discovered.
And the third parameter will be the discovery info that we talked about before.
So if advertiser set discovery info, the browser will get copied version of that dictionary once that callback is called.
In case a peer will get lost, for instance, if it goes out of range of the browser, then you will get notified via a delegate method browser:lostPeer:.
So now let's assume that the browser knows which peers are around and you want what you want to do now is you want to send invitations to these peers to start communicating with them.
Well, to send an invitation, a browser will have to call invitePeer:toSession: method.
And as it does so, an invitation will be sent out to that peer.
When the advertisers when the advertiser receives the invitation, the advertiser's delegate will be notified via a delegate method didReceiveInvitationFromPeer: and the advertiser will be passed an invitation handler.
Same thing happens when we want to invite the advertiser on the right and when it gets the invitation, it gets notified with the same callback method.
And at this point, the advertiser will probably want to consult the user whether the user wants to accept or decline an invitation.
And once it does so, once we know whether an invitation is to be accepted or declined, the advertisers have to call the invitation handler that was just passed to them.
So assuming that the answer was yes, you will have to specify that answer in the first parameter of of the invitation handler.
And the second parameter will be the session object that you want to use for that invitation.
So in code, the invite function looks like this, invitePeer:to Session:withContext: and timeout:.
The first parameter will be the peer ID of the peer that you want to invite.
The session the second parameter will be the session that you want to use for that invitation.
And the third parameter will be context.
This is an NSData parameter that allows you to pass some context along with the invitation.
Timeout here is a value in seconds, which tells the framework how long the browser is willing to wait for the invitation response to come back.
When the advertiser's when the advertiser receives an invitation, you will be notified via this delegate method, advertiser:did ReceiveInvitationFromPeer:with Context: and invitationHandler:.
So the second one will be the peer ID of the of the sender of the invitation.
The third one will be the context if the sender specified some context.
And the fourth one will be a block, an invitation handler that the advertiser will have to call once it knows whether the invitation is to be accepted or declined.
So let's take a look at one possible example of how you can implement this delegate method on the advertiser.
And let's assume that once an invitation comes in, you want to present an alert to the user and you want the alert the user to press to tap Accept or Decline to decide or to notify the framework of the decision or you of the decision.
So first thing you want to do is you want to copy and store the invitation handler because you'll need it later when you find out from the user what it wants to do.
Then you have to instantiate a UIAlertView and you have to specify the title and message for the view for the alert view.
And you want to specify what kind of buttons you want on there.
So let's assume they're Decline and Accept.
Then you're ready to show the alert view.
Once the user taps on either Accept or Decline, you will be notified via a delegate method alertView:clickedButtonAtIndex: and what you want to do first is retrieve the invitation handler.
Then you'll want to extract what the user's decision was, whether to accept or decline.
And once you know that, you'll want to call the invitation handler and passing it the response first and then the session object that you want to use for that invitation.
So again, let's take a look at how you arrive to the point where two session objects are connected with each other if you choose to do programatic discovery.
So as peers get found and lost, browser and advertiser communicate with each other.
The browser's session object is notified of which peer you want to connect to when you called invitePeer:toSession: method.
And the advertiser's session object is notified which peer you want to connect to when you call the invitation handler.
When that happens, much like before, the two session objects will attempt to establish a communication link with each other and when they are done, you will be notified via a delegate method, peer:didChangeState:.
So that was programatic discovery.
Let's now take a look at how you can set up security.
So we provide two things for you.
We provide support for authentication and encryption.
So let's take a look at how you can set up authentication.
To set up authentication, you'll want to instantiate your session object, using a different init method and that method will require you to pass three parameters.
The first one will be the same as before, a peer ID object.
And the second one will be your security identity.
The third one will be encryption preference, which we will handle in more detail when we cover encryption.
So for now, let's take a look at the security identity parameter.
Well, identity is an NSArray object that should adhere to a specific structure, which is depicted on this slide here.
The first object needs to be your identity and that identity needs to be of type SecIdentityRef and that first object is followed by a chain of certificates of type SecCertificateRef that validate your identity.
This is the same format that Secure Transport uses in their APIs.
Now at this point, assuming that you've set up your identity properly and the peer that you want to connect to also set up their identity properly, once the two session objects establish a link with each other, we will exchange the two certificates for you.
And once you receive the certificate from the other peer, your session delegate will be called with session:didReceive Certificate:fromPeer: and you will be passed a certificate handler.
The second parameter will be the certificate of the peer that you're attempting to connect to and this certificate will have the similar structure as the one we've seen before, just the first object will not be a SecIdentityRef, but it will just be a peer certificate of type SecCertificateRef.
The third parameter will tell you the peer ID of of of the nearby peer that the certificate belongs to.
And the fourth parameter will be a certificate handler that you will have to execute once you've decided whether you trust that certificate or not.
So again, it will be on you to make that determination.
Once you get the certificate, you will need to examine it and make the decision whether you trust it or not.
Once you make the decision, you will notify the framework by calling the certificate handler and passing it your response, either a Yes or a No.
So that's how you set up authentication.
Now, let's take a look at encryption.
To set up encryption, you will use the same initializer that we mentioned when we talked about authentication and you will have to use the third parameter, encryption preference.
Now for encryption preference, we provide you with three options.
One is encryption preference None.
If you decide to specify encryption preference None, that will mean that you do not want to encrypt data.
If somebody wants to encrypt data on the other side, you will not be able to connect to that peer.
The second parameter will be Optional and if you specify Optional, that will mean that you are willing to either encrypt or not encrypt data.
You're fine with both options.
The last option is encryption preference Required.
So if you specified encryption preference Required, that will mean that you will be able to communicate only with those peers who have set that option to Required as well, or to Optional.
If a nearby peer set that preference to None, you will not be able to connect to that peer.
And that's really all you need to do to set up encryption and make sure that nobody can eavesdrop on your data in the session.
So to summarize the advanced part, we've covered programatic discovery, we've mentioned that you can use MCNearby advertiser and MCNearby browser classes and using these classes will enable you to handle events, like found peer, lost peer, and do stuff like sending invitations and responding to invitations programatically.
That will enable you to build custom UI if you have the desire to do so.
And also, we've told you how to set up security in your sessions, specifically, we told you how to set up authentication and encryption.
Now I would like to cover some best practices for you.
So I would like to encourage everyone to start advertising on app launch.
So once your app launches, you should start advertising your peers.
Why do you want to do that?
Well if you want the user to press something to make it self-discoverable, then it will inevitably happen that user just won't do that and when somebody will start browsing they won't won't see if somebody's nearby because the other user forgot to tap tap the Make Me Discoverable button or something.
So we encourage you to start advertising on app launch.
Stop advertising when not needed.
So once you've connected into a session, you might not be interested in receiving invitations anymore.
If that is the case, then just stop advertising.
Stop browsing when done.
If you use the framework-supplied UI for browsing that we've covered in the first stage, you don't have to worry about that, because as soon as you dismiss the browser view controller UI, we will stop browsing for you.
On the other hand, if you choose to do programatic browsing, then you will actually have to call stop browsing method to do to stop browsing.
And you might want to do that once you're in the session and you're not interested in any anymore who is around.
Also, we'd like to encourage you to keep discovery info and display names short.
That will optimize networking performance.
And lastly, if you are sending messages and you want these messages to arrive as soon as possible to the recipient, so with minimum possible latency, we encourage you to use unreliable mode and we encourage you to keep these messages short.
Short here means on the order of few 100 bytes.
So for more information, you can talk to Paul Danbold, our evangelist, and you will find more documentation about Multipeer Connectivity in the Multipeer Connectivity Framework Reference.
And, as always, you can meet us at the Apple Developer Forums and you can talk to each other and us if you have further questions.
And that's it.
Thank you very much for coming.
[ Applause ]