Day 84 - 100 Days of Swift
Project 25 (part 2)
Day 84 is the second part of the twenty-fifth project. You review the things you learned about Multipeer Connectivity
and he gives you a few challenges to extend the app. He challenges you to show an alert when a user has disconnected from the network. He challenges you to send text messages through the network. And he challenges you to add a button which shows an alert listing all the users in the session.
For the first challenge I first pulled the code for presenting a basic alert out into its own function, since we’re now using this in multiple places:
1
2
3
4
5
6
7
8
9
10
11
private func presentAlert(title: String? = nil,
message: String? = nil) {
let ac = UIAlertController(title: title,
message: message,
preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK",
style: .default))
DispatchQueue.main.async {
self.present(ac, animated: true)
}
}
Then I updated the current alert to use this function and added another call in the .notConnected
section of the switch statement in peer:didChange
:
1
2
3
4
5
6
7
8
9
// In the catch block of sendImage
let title = "Error Sending Image"
let message = error.localizedDescription
presentAlert(title: title, message: message)
// In the .nonConnected case of session(_:peer:didChange
print("Not Connected: \(peerID.displayName)")
let title = "\(peerID.displayName) Disconnected"
presentAlert(title: title)
This seems to have a bit of delay to it, but it eventually notifies the user when someone has left the network.
The second one required a little more overhead. The first thing I did was add a function for sending some message. It is almost exactly the same as sending an image:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private func sendMessage(_ message: String?) {
guard let mcSession = mcSession else { return }
guard mcSession.connectedPeers.count > 0 else { return }
guard let message = message else { return }
guard let messageData = message.data(using: .utf8) else { return }
do {
try mcSession.send(messageData,
toPeers: mcSession.connectedPeers,
with: .reliable)
} catch {
let title = "Error Sending Message"
let message = error.localizedDescription
presentAlert(title: title, message: message)
}
}
It unwraps all the optionals and then tries to send the message data and presents an alert if something goes wrong.
Then I needed a way for the user to input that message. For simplicity’s sake I just made an alert controller with a textfield and added a UIBarButtonItem
to present it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@objc func composeMessage() {
let ac = UIAlertController(title: "Send a message",
message: "Say whatever you want. Try to keep it nice though.",
preferredStyle: .alert)
var textField: UITextField!
ac.addTextField { textfield in
textField = textfield
textField.placeholder = "Your message"
}
let send = UIAlertAction(title: "Send",
style: .default) { _ in
self.sendMessage(textField.text)
}
ac.addAction(send)
let cancel = UIAlertAction(title: "Cancel",
style: .cancel)
ac.addAction(cancel)
present(ac, animated: true)
}
// In viewDidLoad
let cameraButton = UIBarButtonItem(barButtonSystemItem: .camera,
target: self,
action: #selector(importPicture))
let messageButton = UIBarButtonItem(barButtonSystemItem: .compose,
target: self,
action: #selector(composeMessage))
navigationItem.rightBarButtonItems = [cameraButton, messageButton]
Finally, I just needed a way for the receiving user to see it. Again, I just presented an alert, because that seemed like the simplest way to go about it:
1
2
3
4
5
// In session(_:didRecieve:fromPeer)
else if let message = String(data: data, encoding: .utf8) {
let title = "\(peerID.displayName) Says:"
self?.presentAlert(title: title, message: message)
}
And with that the users can send messages back and forth (albeit in a somewhat cumbersome way):
For the third challenge I added yet another UIBarButtonItem
that calls a function which presents yet another alert with the necessary info in it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@objc func showInfo() {
guard let mcSession = mcSession else { return }
let title = "Session Info"
var message = "Connected Users:\n"
message += mcSession.connectedPeers.map { $0.displayName }.joined(separator: "\n")
presentAlert(title: title, message: message)
}
// In viewDidLoad
let addButton = UIBarButtonItem(barButtonSystemItem: .add,
target: self,
action: #selector(showConnectionPrompt))
let infoButton = UIBarButtonItem(title: "Info", style: .plain, target: self, action: #selector(showInfo))
navigationItem.leftBarButtonItems = [addButton, infoButton]
mcSession = MCSession(peer: peerID,
securityIdentity: nil,
encryptionPreference: .required)
Which leads to something that looks like this:
You can find my version of this project at the end of day 84 on Github here.