Multipeer Connectivity, yaygın veri değişim biçimine bir alternatiftir. Genellikle bir arka uç sunucusu olan bir aracı aracı aracılığıyla Wi-Fi veya Hücresel Ağ üzerinden veri alışverişi yapmak yerine, Multipeer Connectivity aracılar olmadan birden fazla yakındaki cihaz arasında bilgi alışverişi yapma olanağı sağlar.
iPhone ve iPad, Wi-Fi ve Bluetooth teknolojisini kullanırken, MacBook ve Apple TV ise Wi-Fi ve Ethernet'e güveniyor.
Buradan, bu teknolojinin artıları ve eksileri hemen ortaya çıkar. Avantajları arasında merkeziyetsizlik ve buna bağlı olarak aracılar olmadan bilgi alışverişi yapabilme yeteneği yer alır.
Dezavantajları — paylaşım iPhone ve iPad için Wi-Fi ve Bluetooth kapsamıyla veya MacBook ve Apple TV için Wi-Fi ve Ethernet ile sınırlıdır. Başka bir deyişle, bilgi alışverişi cihazların hemen yakınında gerçekleştirilebilir.
Multipeer Connectivity'nin entegrasyonu karmaşık değildir ve aşağıdaki adımlardan oluşur:
Proje ön ayarı
Diğer cihazlar için görünürlüğü ayarlayın
Aralıktaki görünür cihazları tarayın
Veri alışverişi için bir çift cihaz oluşturma
Veri değişimi
Yukarıdaki adımların her birine daha yakından bakalım.
Bu aşamada, proje Multipeer Connectivity'nin uygulanması için hazırlanmalıdır. Bunu yapmak için, tarama yapabilmek için kullanıcıdan ek izinler almanız gerekir:
Info.plist
dosyasına kullanım amacını açıklayan bir açıklamayla ekleyin;Info.plist
aşağıdaki satırlarla da desteklenmesi gerekmektedir:
<key>NSBonjourServices</key> <array> <string>_nearby-devices._tcp</string> <string>_nearby-devices._upd</string> </array>
Bu bağlamda nearby-devices
alt dizesinin bir örnek olarak kullanıldığını belirtmek önemlidir. Projenizde bu anahtar aşağıdaki gereksinimleri karşılamalıdır:
1–15 karakter uzunluğunda ve geçerli karakterler arasında en az bir harf içeren ve bitişik tire içermeyen ASCII küçük harfleri, sayılar ve tire bulunur.
Gereksinimler hakkında daha fazla bilgiyi burada okuyabilirsiniz__.
İletişim protokollerine gelince, örnekte tcp
ve upd
(daha güvenilir ve daha az güvenilir) kullanılmıştır. Hangi protokole ihtiyacınız olduğunu bilmiyorsanız, her ikisini de girmelisiniz.
Çoklu eş bağlantısı için cihaz görünürlüğünün organizasyonu MCNearbyServiceAdvertiser
tarafından uygulanır. Cihazlar arasında bilgi algılama, görüntüleme ve paylaşmaktan sorumlu olacak bir sınıf oluşturalım.
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 ) } }
Multipeer'ın çekirdeğini, cihazlar arasında bağlantı kurmanızı ve veri alışverişinde bulunmanızı sağlayacak olan MCSession
oluşturur.
serviceType
yukarıda bahsi geçen anahtardır ve değişim protokolleriyle birlikte Info.plist
dosyasına eklenmiştir.
isAdvertised
özelliği, Toggle
kullanarak cihazın görünürlüğünü değiştirmenize olanak tanır.
Çoklu eş bağlantısı için cihaz görünürlüğü taraması MCNearbyServiceBrowser
tarafından gerçekleştirilir:
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 }
Görünür tüm aygıtların bir listesi peers
saklanacaktır. MCNearbyServiceBrowser
temsilci yöntemleri, bir eş bulunduğunda veya kaybolduğunda bir MCPeerID
ekleyecek veya kaldıracaktır.
Ekran göründüğünde görünür cihazları keşfetmeye başlamak veya ekran kaybolduktan sonra aramayı durdurmak için startBrowsing
ve finishBrowsing
yöntemleri kullanılacaktır.
Aşağıdaki View
UI olarak kullanılacaktır:
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) } } } } }
Cihaz görünürlüğü Toggle
ile etkinleştirilecek/devre dışı bırakılacak.
Sonuç olarak bu aşamada cihazların tespiti ve görüntülenmesinin doğru bir şekilde çalışması gerekmektedir.
Temsilci yöntemi MCNearbyServiceAdvertiserdidReceiveInvitationFromPeer
, bir cihaz çifti arasında bir davet göndermekten sorumludur. Her ikisi de bu isteği işleyebilmelidir.
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 }
selectedPeer
ayarlandığında, connect yöntemi tetiklenir. Bu peer
mevcut peers
listesindeyse, joinedPeer
dizisine eklenir. Gelecekte, bu özellik UI tarafından işlenecektir.
Bu eşin oturumda bulunmaması durumunda browser
bu cihazı bir çift oluşturmaya davet edecektir.
Bundan sonra, didReceiveInvitationFromPeer
yöntemi davet edilen cihaz için işlenecektir. Bizim durumumuzda, didReceiveInvitationFromPeer
başlamasından sonra, davet edilen cihazda bir uyarı olarak gösterilecek gecikmeli bir geri arama ile bir permissionRequest
oluşturulur:
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) }) ) }) ... } } }
Onay durumunda didReceiveInvitationFromPeer
, daveti gönderen cihazı, izni ve izin başarılıysa oturumu döndürecektir.
Sonuç olarak daveti başarıyla kabul ettikten sonra bir çift oluşturulacak:
Bir çift oluşturulduktan sonra, MCSession
verilerin değişiminden sorumludur:
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) } }
func send(_ data: Data, toPeers peerIDs: [MCPeerID], with mode: MCSessionSendDataMode) throws
metodu eşler arasında veri göndermeye yardımcı olur.
Mesajı alan cihazda temsilci yöntemi func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID)
tetiklenir.
Ayrıca, MCSession
temsilci yöntemleri DispatchQueue global()
içinde ateşlendiğinden, mesajları almak için bir ara yayıncı messagePublisher
kullanılır.
Multipeer Bağlantı entegrasyon prototipi hakkında daha fazla ayrıntıyı burada bulabilirsiniz
Bana ulaşmaktan çekinmeyin