Skip to content

Instantly share code, notes, and snippets.

@irace
Last active April 17, 2019 15:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save irace/925450c6feca746dad7cced8d5375f76 to your computer and use it in GitHub Desktop.
Save irace/925450c6feca746dad7cced8d5375f76 to your computer and use it in GitHub Desktop.
Nearby user discovery through MultipeerConnectivity in Swift
import Foundation
/**
Extensions that allow `JSONEncoder` and `JSONDecoder` to produce/consume dictionaries, instead of simply raw data.
*/
extension JSONEncoder {
func encodeAsJSONObject<T>(_ value: T) throws -> Any where T: Encodable {
return try JSONSerialization.jsonObject(with: try encode(value), options: [])
}
}
extension JSONDecoder {
func decode<T>(_ type: T.Type, fromJSONObject object: Any) throws -> T where T: Decodable {
return try decode(T.self, from: try JSONSerialization.data(withJSONObject: object, options: []))
}
}
import Foundation
import MultipeerConnectivity
typealias DiscoveryInfo = [String: String]
protocol PeerConnectorDelegate: class {
func peerConnectorFoundPeer(id: MCPeerID, withDiscoveryInfo info: DiscoveryInfo?)
func peerConnectorLostPeer(id: MCPeerID)
}
final class PeerConnector: NSObject {
private let peerID: MCPeerID
private let serviceAdvertiser: MCNearbyServiceAdvertiser?
private let serviceBrowser: MCNearbyServiceBrowser
// MARK: - Inputs
weak var delegate: PeerConnectorDelegate?
// MARK: - Initialization
init(displayName: String, discoveryInfo: DiscoveryInfo?) {
self.peerID = MCPeerID(displayName: displayName)
// Must be a unique, at most 15 characters long, and contain only ASCII lowercase letters, numbers and hyphens
let serviceType = "my-service"
if let discoveryInfo = discoveryInfo {
serviceAdvertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: discoveryInfo, serviceType: serviceType)
}
else {
serviceAdvertiser = nil
}
serviceBrowser = MCNearbyServiceBrowser(peer: peerID, serviceType: serviceType)
super.init()
serviceBrowser.delegate = self
}
func start() {
serviceAdvertiser?.startAdvertisingPeer()
serviceBrowser.startBrowsingForPeers()
}
func stop() {
serviceAdvertiser?.stopAdvertisingPeer()
serviceBrowser.stopBrowsingForPeers()
}
}
// MARK: - MCNearbyServiceBrowserDelegate
extension PeerConnector: MCNearbyServiceBrowserDelegate {
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String: String]?) {
log.info("Found peer: \(peerID)")
delegate?.peerConnectorFoundPeer(id: peerID, withDiscoveryInfo: info)
}
func browser(_ browser: MCNearbyServiceBrowser, didNotStartBrowsingForPeers error: Error) {
log.warn("Did not start browsing for peers: \(error)")
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
log.info("Lost peer: \(peerID)")
delegate?.peerConnectorLostPeer(id: peerID)
}
}
import Foundation
struct PeerDiscoveryInfo: Codable {
let id: String
let firstName: String
let lastName: String?
let mediumAvatar: String?
let jobTitle: String?
}
extension User {
init?(discoveryInfo: DiscoveryInfo?) {
guard
let discoveryInfo = discoveryInfo,
let peerDiscoveryInfo = try? JSONDecoder().decode(PeerDiscoveryInfo.self, fromJSONObject: discoveryInfo),
let id = Int(peerDiscoveryInfo.id)
else {
return nil
}
self.init(
id: id,
firstName: peerDiscoveryInfo.firstName,
lastName: peerDiscoveryInfo.lastName,
avatar: peerDiscoveryInfo.mediumAvatar
.map(URL.init)
.map({ RemoteImage(medium: $0 )}),
jobTitle: peerDiscoveryInfo.jobTitle
.map({ JobTitle(name: $0, id: nil) })
)
}
func toDiscoveryInfo() -> DiscoveryInfo? {
let peerDiscoveryInfo = PeerDiscoveryInfo(
id: String(id),
firstName: firstName,
lastName: lastName,
mediumAvatar: avatar?.medium?.absoluteString,
jobTitle: jobTitle?.name
)
return (try? JSONEncoder().encodeAsJSONObject(peerDiscoveryInfo))
.flatMap({ $0 as? DiscoveryInfo })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment