Getting Started with Multipeer Connectivity
In this tutorial, you’ll learn how to transfer data between devices with no external network. You’ll also try your hand at creating a chat feature. By Andy Pereira.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Getting Started with Multipeer Connectivity
30 mins
Binding the UI
Next, in JobListView.swift, add the following property:
@ObservedObject var jobConnectionManager: JobConnectionManager
Then replace the initializer with:
init(jobListStore: JobListStore = JobListStore()) {
self.jobListStore = jobListStore
jobConnectionManager = JobConnectionManager { job in
jobListStore.jobs.append(job)
}
}
In the code above, you’re doing two things:
- Adding an observed property to manage your advertising state.
- Initializing the same property.
JobConnectionManager
will send a job through this closure when it receives one. This code will append that received job tojobListStore
, which will get used a little later on.
Finally, replace headerView
in JobListView.swift with the following:
var headerView: some View {
Toggle("Receive Jobs", isOn: $jobConnectionManager.isReceivingJobs)
}
By binding your Toggle
to isReceivingJobs
, your app will now turn advertising on and off.
Now remove the following property from JobListView.swift, as it’s no longer used:
@State private var isReceivingJobs = false
Build and run, and turn the RECEIVE JOBS toggle on and off. You’ll see the following in the Xcode Console:
Discovering Devices
Now that you have the ability to advertise a device, the next step is to discover devices. Start by adding the following properties to JobConnectionManager.swift:
@Published var employees: [MCPeerID] = []
private var nearbyServiceBrowser: MCNearbyServiceBrowser
The first new property, employees
, stores the devices discovered through the service you set up in the previous section.
The next property is MCNearbyServiceBrowser
. This class handles all the work of discovering devices that have turned on advertising.
Next, in the initializer for JobConnectionManager
, initialize nearbyServiceBrowser
before the call to super
:
nearbyServiceBrowser = MCNearbyServiceBrowser(
peer: myPeerId,
serviceType: JobConnectionManager.service)
MCNearbyServiceBrowser
looks similar to MCNearbyServiceAdvertiser
. It requires a peer ID and a service. If both of these properties use JobConnectionManager.service
, they’ll be able to discover and communicate with each other.
Add the following to the end of the JobConnectionManager.swift:
extension JobConnectionManager: MCNearbyServiceBrowserDelegate {
func browser(
_ browser: MCNearbyServiceBrowser,
foundPeer peerID: MCPeerID,
withDiscoveryInfo info: [String: String]?
) {
// 1
if !employees.contains(peerID) {
employees.append(peerID)
}
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
guard let index = employees.firstIndex(of: peerID) else { return }
// 2
employees.remove(at: index)
}
}
Implementing MCNearbyServiceBrowserDelegate
allows you to respond to events when peers or devices are discovered.
Two things are happening here:
- When the browser discovers a peer, it adds it to
employees
. - Conversely, when the browser looses a peer, it removes it from the list.
Now, add the following to the end of the initializer of JobConnectionManager
:
nearbyServiceBrowser.delegate = self
This ensures your code from the previous step will be called as devices are discovered or lost.
Before you can set up the UI to show devices, add the following methods to enable and disable browsing in JobConnectionManager
:
func startBrowsing() {
nearbyServiceBrowser.startBrowsingForPeers()
}
func stopBrowsing() {
nearbyServiceBrowser.stopBrowsingForPeers()
}
By default, your service browser won’t be browsing for peers. You can control the state by starting and stopping this service.
Rendering the List
To render the list, start by adding the following property to both JobView.swift and JobListRowView.swift:
@EnvironmentObject var jobConnectionManager: JobConnectionManager
Since you want to share model data between the job views, set up your property as an EnvironmentObject
.
Next, in JobListView.swift, in body
, replace the Section
‘s body with the following:
ForEach(jobListStore.jobs) { job in
JobListRowView(job: job)
.environmentObject(jobConnectionManager)
}
This is now sharing your jobConnectionManager
with JobListRowView
.
You’ll need to set jobConnectionManager
on one more view. In JobListRowView.swift, replace the destination
of the NavigationLink
with the following:
JobView(job: job).environmentObject(jobConnectionManager)
Like in the last step, it’s sharing jobConnectionManager
with JobView
.
Again, you’re almost there!
In JobView.swift, replace EmptyView()
, which you’ll find in the last Section
, with the following:
ForEach(jobConnectionManager.employees, id: \.self) { employee in
HStack {
Text(employee.displayName)
.font(.headline)
Spacer()
Image(systemName: "arrowshape.turn.up.right.fill")
}
}
This will populate the section with every employee found while browsing. It will then add and remove them to and from this list as they’re discovered or lost.
Finally, in JobView.swift, add the following to the end of the List
after setting the navigation title:
.onAppear {
jobConnectionManager.startBrowsing()
}
.onDisappear {
jobConnectionManager.stopBrowsing()
}
When the job view appears, it’ll start browsing for peers, and it’ll stop when the view disappears. You’re all set!
Build and run on two different devices. You’ll see the following:
On one device, add a job, and then select this job from the main list.
On the other device, turn on RECEIVE JOBS. You’ll now see the following:
Finally, turn off RECEIVE JOBS from the second device, which removes the peer from the list of the first device:
Inviting Peers
Now the bulk of the work is complete, you’re ready to start sending jobs between devices. The first step is to wire up inviting devices to connect to each other.
Open JobConnectionManager.swift and add a new property:
private var jobToSend: JobModel?
When two devices connect, it happens asynchronously and through delegate methods. This property will temporarily save the job you’re sending.
Next, add the following method to the class:
func invitePeer(_ peerID: MCPeerID, to job: JobModel) {
// 1
jobToSend = job
// 2
let context = job.name.data(using: .utf8)
// 3
nearbyServiceBrowser.invitePeer(
peerID,
to: session,
withContext: context,
timeout: TimeInterval(120))
}
Here’s a breakdown of what you added, step by step:
- Save the job until it’s needed
- Create a context. This is serializing the job’s name into
Data
. - Ask your service browser invite a peer using the other device’s peer ID, passing the serialized job name as the context
Inviting a peer from one device will prompt the other device to connect using advertiser(_:didReceiveInvitationFromPeer:withContext:invitationHandler:)
, which you added earlier on. The browsing device creates the context, and the advertising peer receives it to help it determine if it wants to accept the job.
In JobView.swift, add a tap gesture to the rows that display nearby devices by adding the following to the end of the HStack
that makes up the row:
.onTapGesture {
jobConnectionManager.invitePeer(employee, to: job)
}
Tapping a nearby device or peer from the list will trigger the invitation process.
Build and run. Again, create a job on one device, and turn on RECEIVE JOBS on the other. Select the job from the main list and select the nearby device. You’ll see an invitation show up on the second device, like below: