paint-brush
Meistern Sie die iOS-Multipeer-Konnektivität und teilen Sie Daten über mehrere Geräte hinweg ohne Internetzugangvon@bugorbn
251 Lesungen

Meistern Sie die iOS-Multipeer-Konnektivität und teilen Sie Daten über mehrere Geräte hinweg ohne Internetzugang

von Boris Bugor12m2024/08/19
Read on Terminal Reader

Zu lang; Lesen

Multipeer Connectivity ermöglicht den direkten Datenaustausch zwischen Apple-Geräten über WLAN, Bluetooth und Ethernet und umgeht dabei herkömmliche Server. Der Artikel beschreibt die Vorteile, Einschränkungen und Schritte zur Integration dieser Technologie in Ihre Projekte.
featured image - Meistern Sie die iOS-Multipeer-Konnektivität und teilen Sie Daten über mehrere Geräte hinweg ohne Internetzugang
Boris Bugor HackerNoon profile picture
0-item


Multipeer Connectivity ist eine Alternative zum üblichen Datenaustauschformat. Anstatt Daten über WLAN oder Mobilfunknetz über einen Zwischenbroker auszutauschen, bei dem es sich normalerweise um einen Backend-Server handelt, bietet Multipeer Connectivity die Möglichkeit, Informationen zwischen mehreren Geräten in der Nähe ohne Zwischenhändler auszutauschen.


iPhone und iPad nutzen Wi-Fi- und Bluetooth-Technologie, während MacBook und Apple TV auf Wi-Fi und Ethernet angewiesen sind.


Von hier aus ergeben sich unmittelbar die Vor- und Nachteile dieser Technologie. Zu den Vorteilen zählen die Dezentralisierung und dementsprechend die Möglichkeit, Informationen ohne Zwischenhändler auszutauschen.


Nachteile: Die Freigabe ist auf WLAN- und Bluetooth-Abdeckung für iPhone und iPad bzw. WLAN und Ethernet für MacBook und Apple TV beschränkt. Mit anderen Worten: Der Informationsaustausch kann nur in unmittelbarer Nähe der Geräte erfolgen.


Die Integration der Multipeer-Konnektivität ist nicht kompliziert und umfasst die folgenden Schritte:

  1. Projektvorgabe

  2. Sichtbarkeit für andere Geräte einrichten

  3. Nach sichtbaren Geräten in Reichweite suchen

  4. Erstellen eines Gerätepaares für den Datenaustausch

  5. Datenaustausch


Sehen wir uns jeden der oben genannten Schritte genauer an.


1. Projektvorgabe

In dieser Phase muss das Projekt für die Implementierung der Multipeer-Konnektivität vorbereitet werden. Dazu müssen Sie zusätzliche Berechtigungen vom Benutzer einholen, um scannen zu können:

  • Fügen Sie der Datei Info.plist „Privacy – Local Network Usage Description“ mit einer Beschreibung des Verwendungszwecks hinzu;
  • Um einen Informationsaustausch zu ermöglichen, muss Info.plist zusätzlich noch um folgende Zeilen ergänzt werden:


 <key>NSBonjourServices</key> <array> <string>_nearby-devices._tcp</string> <string>_nearby-devices._upd</string> </array>


Es ist wichtig zu beachten, dass die Teilzeichenfolge nearby-devices in diesem Kontext als Beispiel verwendet wird. In Ihrem Projekt muss dieser Schlüssel die folgenden Anforderungen erfüllen:

Es ist 1–15 Zeichen lang und umfasst als gültige Zeichen ASCII-Kleinbuchstaben, Zahlen und den Bindestrich. Es muss mindestens ein Buchstabe vorhanden sein und darf keine Bindestriche neben dem Buchstaben enthalten.

Nähere Informationen zu den Voraussetzungen__ können Sie hier__ nachlesen.

Als Kommunikationsprotokolle verwendet das Beispiel tcp und upd (ein zuverlässigeres und ein weniger zuverlässiges). Wenn Sie nicht wissen, welches Protokoll Sie benötigen, sollten Sie beide eingeben.

2. Sichtbarkeit für andere Geräte einrichten

Die Organisation der Gerätesichtbarkeit für Multi-Peer-Verbindungen wird von MCNearbyServiceAdvertiser implementiert. Lassen Sie uns eine Klasse erstellen, die für das Erkennen, Anzeigen und Teilen von Informationen zwischen Geräten verantwortlich ist.


 import MultipeerConnectivity import SwiftUI class DeviceFinderViewModel: ObservableObject { private let advertiser: MCNearbyServiceAdvertiser private let session: MCSession private let serviceType = "nearby-devices" @Published var isAdvertised: Bool = false { didSet { isAdvertised ? advertiser.startAdvertisingPeer() : advertiser.stopAdvertisingPeer() } } init() { let peer = MCPeerID(displayName: UIDevice.current.name) session = MCSession(peer: peer) advertiser = MCNearbyServiceAdvertiser( peer: peer, discoveryInfo: nil, serviceType: serviceType ) } }


Der Kern des Multipeers ist eine MCSession , die Ihnen die Verbindung und den Datenaustausch zwischen Geräten ermöglicht.

Der serviceType ist der oben erwähnte Schlüssel, der zusammen mit den Austauschprotokollen zur Datei Info.plist hinzugefügt wurde.

Mit der Eigenschaft isAdvertised können Sie die Sichtbarkeit des Geräts mithilfe von Toggle umschalten.

3. Nach sichtbaren Geräten in Reichweite suchen

Die Gerätesichtbarkeitsprüfung für eine Multi-Peer-Verbindung wird von MCNearbyServiceBrowser durchgeführt:


 class DeviceFinderViewModel: NSObject, ObservableObject { ... private let browser: MCNearbyServiceBrowser ... @Published var peers: [PeerDevice] = [] ... override init() { ... browser = MCNearbyServiceBrowser(peer: peer, serviceType: serviceType) super.init() browser.delegate = self } func startBrowsing() { browser.startBrowsingForPeers() } func finishBrowsing() { browser.stopBrowsingForPeers() } } extension DeviceFinderViewModel: MCNearbyServiceBrowserDelegate { func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { peers.append(PeerDevice(peerId: peerID)) } func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) { peers.removeAll(where: { $0.peerId == peerID }) } } struct PeerDevice: Identifiable, Hashable { let id = UUID() let peerId: MCPeerID }


Eine Liste aller sichtbaren Geräte wird in peers gespeichert. Die MCNearbyServiceBrowser Delegatmethoden fügen eine MCPeerID hinzu oder entfernen sie, wenn ein Peer gefunden wird oder verloren geht.


Die Methoden startBrowsing und finishBrowsing werden verwendet, um mit der Erkennung sichtbarer Geräte zu beginnen, wenn der Bildschirm erscheint, bzw. die Suche zu beenden, nachdem der Bildschirm verschwindet.


Die folgende View wird als Benutzeroberfläche verwendet:


 struct ContentView: View { @StateObject var model = DeviceFinderViewModel() var body: some View { NavigationStack { List(model.peers) { peer in HStack { Image(systemName: "iphone.gen1") .imageScale(.large) .foregroundColor(.accentColor) Text(peer.peerId.displayName) .frame(maxWidth: .infinity, alignment: .leading) } .padding(.vertical, 5) } .onAppear { model.startBrowsing() } .onDisappear { model.finishBrowsing() } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Toggle("Press to be discoverable", isOn: $model.isAdvertised) .toggleStyle(.switch) } } } } }


Die Gerätesichtbarkeit wird durch den Toggle aktiviert/deaktiviert.
Als Ergebnis sollte zu diesem Zeitpunkt die Erkennung und Anzeige von Geräten ordnungsgemäß funktionieren.


4. Erstellen eines Gerätepaares für den Datenaustausch

Die Delegatmethode MCNearbyServiceAdvertiserdidReceiveInvitationFromPeer ist für das Senden einer Einladung zwischen zwei Geräten verantwortlich. Beide Geräte müssen in der Lage sein, diese Anforderung zu verarbeiten.


 class DeviceFinderViewModel: NSObject, ObservableObject { ... @Published var permissionRequest: PermitionRequest? @Published var selectedPeer: PeerDevice? { didSet { connect() } } ... @Published var joinedPeer: [PeerDevice] = [] override init() { ... advertiser.delegate = self } func startBrowsing() { browser.startBrowsingForPeers() } func finishBrowsing() { browser.stopBrowsingForPeers() } func show(peerId: MCPeerID) { guard let first = peers.first(where: { $0.peerId == peerId }) else { return } joinedPeer.append(first) } private func connect() { guard let selectedPeer else { return } if session.connectedPeers.contains(selectedPeer.peerId) { joinedPeer.append(selectedPeer) } else { browser.invitePeer(selectedPeer.peerId, to: session, withContext: nil, timeout: 60) } } } extension DeviceFinderViewModel: MCNearbyServiceAdvertiserDelegate { func advertiser( _ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void ) { permissionRequest = PermitionRequest( peerId: peerID, onRequest: { [weak self] permission in invitationHandler(permission, permission ? self?.session : nil) } ) } } struct PermitionRequest: Identifiable { let id = UUID() let peerId: MCPeerID let onRequest: (Bool) -> Void }


Wenn selectedPeer gesetzt ist, wird die Methode connect ausgelöst. Wenn dieser peer in der Liste der vorhandenen peers enthalten ist, wird er dem Array joinedPeer hinzugefügt. In Zukunft wird diese Eigenschaft von der Benutzeroberfläche verarbeitet.


Wenn dieser Peer in der Sitzung nicht vorhanden ist, lädt der browser dieses Gerät ein, ein Paar zu erstellen.


Anschließend wird die Methode didReceiveInvitationFromPeer für das eingeladene Gerät ausgeführt. In unserem Fall wird nach dem Start von didReceiveInvitationFromPeer ein permissionRequest mit verzögertem Callback erstellt, der als Alarm auf dem eingeladenen Gerät angezeigt wird:


 struct ContentView: View { @StateObject var model = DeviceFinderViewModel() var body: some View { NavigationStack { ... .alert(item: $model.permissionRequest, content: { request in Alert( title: Text("Do you want to join \(request.peerId.displayName)"), primaryButton: .default(Text("Yes"), action: { request.onRequest(true) model.show(peerId: request.peerId) }), secondaryButton: .cancel(Text("No"), action: { request.onRequest(false) }) ) }) ... } } }


Im Falle einer Genehmigung gibt didReceiveInvitationFromPeer das Gerät zurück, das die Einladung, die Berechtigung und die Sitzung gesendet hat, wenn die Berechtigung erfolgreich war.


Als Ergebnis wird nach erfolgreicher Annahme der Einladung ein Paar erstellt:


5. Datenaustausch

Nach der Paarbildung ist MCSession für den Datenaustausch zuständig:


 import MultipeerConnectivity import Combine class DeviceFinderViewModel: NSObject, ObservableObject { ... @Published var messages: [String] = [] let messagePublisher = PassthroughSubject<String, Never>() var subscriptions = Set<AnyCancellable>() func send(string: String) { guard let data = string.data(using: .utf8) else { return } try? session.send(data, toPeers: [joinedPeer.last!.peerId], with: .reliable) messagePublisher.send(string) } override init() { ... session.delegate = self messagePublisher .receive(on: DispatchQueue.main) .sink { [weak self] in self?.messages.append($0) } .store(in: &subscriptions) } } extension DeviceFinderViewModel: MCSessionDelegate { func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { guard let last = joinedPeer.last, last.peerId == peerID, let message = String(data: data, encoding: .utf8) else { return } messagePublisher.send(message) } }


Die Methode func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode) throws hilft beim Senden von Daten zwischen Peers.


Die Delegatmethode func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) wird auf dem Gerät ausgelöst, das die Nachricht empfangen hat.


Außerdem wird ein Zwischenherausgeber messagePublisher zum Empfangen von Nachrichten verwendet, da die MCSession Delegatmethoden in der DispatchQueue global() ausgelöst werden.


Weitere Einzelheiten zum Multipeer Connectivity-Integrationsprototyp finden Sie hier Endlager . Diese Technologie ermöglicht beispielsweise den Nachrichtenaustausch zwischen Geräten.



Zögern Sie nicht, mich zu kontaktieren unter Þjórsárden wenn Sie Fragen haben. Sie können auch jederzeit kauf mir einen Kaffee .