Tradicionalmente, conectar dispositivos para comunicações ponto a ponto tem sido um trabalho pesado. Um aplicativo precisa descobrir o que está ao seu redor, abrir conexões em ambos os lados e, em seguida, mantê-los como infraestrutura de rede, conexões, distâncias, etc, tudo muda. Percebendo as dificuldades inerentes a essas atividades, no iOS 7 e no macOS 10.10 a Apple apresentou seu Estrutura MultipeerConnectivity (doravante MPC), projetado para permitir que os aplicativos executem essas tarefas com um esforço relativamente baixo.
MPC cuida de grande parte da infraestrutura necessária subjacente aqui:
Neste artigo, abordaremos principalmente a implementação do iOS, mas a maioria, senão tudo, é aplicável ao macOS e tvOS.
Ciclo de vida da sessão multipeer:
MCNearbyServiceAdvertiser.startAdvertisingForPeers()
MCNearbyServiceBrowser.startBrowsingForPeers()
MCNearbyServiceBrowserDelegate.browser(:foundPeer:withDiscoveryInfo:)
MCNearbyServiceBrowser.invitePeer(...)
MCNearbyServiceAdvertiserDelegate.didReceiveInvitationFromPeer(...)
invitationHandler
em didReceiveInvitation
Create the MCSession
MCSession.send(...)
MCSessionDelegate.session(_:didReceive:data:peerID)
MCSession.disconnect()
Consulte esta imagem de vez em quando
Existem inúmeros tutoriais e exemplos do MultipeerConnectivity por aí que pretendem caminhar desenvolvedores iOS por meio da implementação de um aplicativo baseado em MPC. No entanto, na minha experiência, eles geralmente são incompletos e tendem a encobrir alguns obstáculos potenciais importantes com o MPC. Neste artigo, espero guiar o leitor por uma implementação rudimentar de tal aplicativo e apontar áreas onde eu achei fácil travar.
MPC é baseado em um punhado de classes. Vamos examinar a lista dos mais comuns e aumentar nossa compreensão da estrutura.
MCSession
- Uma sessão gerencia todas as comunicações entre seus pares associados. Você pode enviar mensagens, arquivos e fluxos por meio de uma sessão, e seu delegado será notificado quando um deles for recebido de um par conectado.MCPeerID
- Um ID de par permite que você identifique dispositivos de par individuais em uma sessão. Tem um nome associado a ele, mas tenha cuidado: IDs de mesmo nome não são considerados idênticos (consulte Regras básicas, abaixo).MCNearbyServiceAdvertiser
- Um anunciante permite que você transmita o nome do seu serviço para dispositivos próximos. Isso permite que eles se conectem a você.MCNearbyServiceBrowser
- Um navegador permite pesquisar dispositivos usando MCNearbyServiceAdvertiser
. Usar essas duas classes juntas permite que você descubra dispositivos próximos e crie suas conexões ponto a ponto.MCBrowserViewController
- Isso fornece uma IU muito básica para navegar pelos serviços do dispositivo próximo (vendido via MCNearbyServiceAdvertiser
). Embora seja adequado para alguns casos de uso, não o usaremos, pois, em minha experiência, um dos melhores aspectos do MCP é sua perfeição.Há algumas coisas a se ter em mente ao construir uma rede MPC:
Antes de estabelecermos nossa rede, precisamos fazer algumas tarefas domésticas e, em seguida, configurar as classes de anunciante e navegador para descobrir outros dispositivos com os quais podemos nos comunicar. Vamos criar um singleton que usaremos para manter algumas variáveis de estado (nosso MCPeerID local e quaisquer dispositivos conectados), então criaremos MCNearbyServiceAdvertiser
e MCNearbyServiceBrowser
. Esses dois últimos objetos precisam de um tipo de serviço, que é apenas uma string que identifica seu aplicativo. Deve ter menos de 16 caracteres e deve ser o mais exclusivo possível (ou seja, “MyApp-MyCo”, não “Multipeer”). Podemos especificar um (pequeno) dicionário para nosso anunciante do que os navegadores podem ler para fornecer um pouco mais de informação ao olhar para dispositivos próximos (talvez um tipo de jogo ou função do dispositivo).
Como o MPC depende de APIs fornecidas pelo sistema e se correlaciona com objetos do mundo real (outros dispositivos, bem como a 'rede' compartilhada entre eles), é um bom ajuste para o padrão singleton. Embora freqüentemente usados em excesso, os singletons são uma boa opção para recursos compartilhados como este.
Aqui está a definição do nosso singleton:
class MPCManager: NSObject { var advertiser: MCNearbyServiceAdvertiser! var browser: MCNearbyServiceBrowser! static let instance = MPCManager() let localPeerID: MCPeerID let serviceType = 'MPC-Testing' var devices: [Device] = [] override init() { if let data = UserDefaults.standard.data(forKey: 'peerID'), let id = NSKeyedUnarchiver.unarchiveObject(with: data) as? MCPeerID { self.localPeerID = id } else { let peerID = MCPeerID(displayName: UIDevice.current.name) let data = try? NSKeyedArchiver.archivedData(withRootObject: peerID) UserDefaults.standard.set(data, forKey: 'peerID') self.localPeerID = peerID } super.init() self.advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: self.serviceType) self.advertiser.delegate = self self.browser = MCNearbyServiceBrowser(peer: localPeerID, serviceType: self.serviceType) self.browser.delegate = self } }
Observe que estamos armazenando nosso MCPeerID
nos padrões do usuário (por meio de NSKeyedArchiver
) e reutilizá-lo. Como mencionado acima, isso é importante, e deixar de armazená-lo em cache de alguma forma pode causar bugs obscuros mais adiante na linha.
Esta é nossa classe de dispositivos, que usaremos para rastrear quais dispositivos foram descobertos e qual é seu estado:
class Device: NSObject { let peerID: MCPeerID var session: MCSession? var name: String var state = MCSessionState.notConnected init(peerID: MCPeerID) { self.name = peerID.displayName self.peerID = peerID super.init() } func invite() { browser.invitePeer(self.peerID, to: self.session!, withContext: nil, timeout: 10) } }
Agora que construímos nossas aulas iniciais, é hora de voltar atrás e pensar sobre a interação entre navegadores e anunciantes. No MPC, um dispositivo pode anunciar um serviço que oferece e pode procurar um serviço de seu interesse em outros dispositivos. Como estamos focados na comunicação de dispositivo a dispositivo usando apenas nosso aplicativo, vamos anunciar e procurar o mesmo serviço.
Em uma configuração cliente / servidor tradicional, um dispositivo (o servidor) anunciaria seus serviços e o cliente os procuraria. Como somos igualitários, não queremos ter que especificar funções para nossos dispositivos; teremos todos os dispositivos para anunciar e navegar.
Precisamos adicionar um método ao nosso MPCManager
para criar dispositivos conforme eles são descobertos e rastreá-los em nossa gama de dispositivos. Nosso método pegará um MCPeerID
, procurará por um dispositivo existente com aquele ID e o retornará se encontrado. Se ainda não temos um dispositivo existente, criamos um novo e o adicionamos ao nosso conjunto de dispositivos.
func device(for id: MCPeerID) -> Device { for device in self.devices { if device.peerID == id { return device } } let device = Device(peerID: id) self.devices.append(device) return device }
Depois que um dispositivo começa a anunciar, outro dispositivo de navegação pode tentar se conectar a ele. Precisamos adicionar métodos delegados ao nosso MPCSession
classe para lidar com chamadas de delegado recebidas de nosso anunciante neste caso:
extension MPCManager: MCNearbyServiceAdvertiserDelegate { func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) { let device = MPCManager.instance.device(for: peerID) device.connect() invitationHandler(true, device.session) } }
… Um método em nosso dispositivo para criar a MCSession:
func connect() { if self.session != nil { return } self.session = MCSession(peer: MPCManager.instance.localPeerID, securityIdentity: nil, encryptionPreference: .required) self.session?.delegate = self }
… E, finalmente, um método para acionar o convite quando nosso navegador descobrir um anunciante:
extension MPCManager: MCNearbyServiceBrowserDelegate { func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { let device = MPCManager.instance.device(for: peerID) device.invite(with: self.browser) }
No momento, estamos ignorando o withDiscoveryInfo
argumento; poderíamos usar isso para filtrar dispositivos específicos com base no que eles disponibilizaram (este é o mesmo dicionário que fornecemos no discoveryInfo
argumento para MCNearbyServiceAdvertiser
, acima).
Agora que cuidamos de todas as nossas tarefas domésticas, podemos começar o negócio real de conectar dispositivos.
No método init de nossa MPCSession, configuramos nosso anunciante e nosso delegado. Quando estivermos prontos para iniciar a conexão, precisaremos iniciar os dois. Isso pode ser feito no método didFinishLaunching do delegado do aplicativo ou sempre que for apropriado. Aqui está o start()
método que adicionaremos à nossa classe:
func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() }
Essas chamadas significarão que seu aplicativo começará a transmitir sua presença por WiFi. Observe que você não precisa estar conectado a uma rede WiFi para que isso funcione (mas você precisa tê-lo ligado).
Quando um dispositivo responde a um convite e inicia sua MCSession, ele começa a receber callbacks delegados da sessão. Vamos adicionar manipuladores para aqueles em nosso objeto de dispositivo; a maioria deles vamos ignorar por enquanto:
extension Device: MCSessionDelegate { public func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { self.state = state NotificationCenter.default.post(name: Multipeer.Notifications.deviceDidChangeState, object: self) } public func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { } public func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { } }
Por enquanto, estamos principalmente preocupados com session(_:peer:didChangeState:)
ligue de volta. Será chamado sempre que um dispositivo fizer a transição para um novo estado (notConnected
, connecting
e connected
). Queremos acompanhar isso para que possamos construir uma lista de todos os dispositivos conectados:
extension MPCManager { var connectedDevices: [Device] { return self.devices.filter { Conluio: Rede de Dispositivos Próximos com MultipeerConnectivity no iOS
Tradicionalmente, conectar dispositivos para comunicações ponto a ponto tem sido um trabalho pesado. Um aplicativo precisa descobrir o que está ao seu redor, abrir conexões em ambos os lados e, em seguida, mantê-los como infraestrutura de rede, conexões, distâncias, etc, tudo muda. Percebendo as dificuldades inerentes a essas atividades, no iOS 7 e no macOS 10.10 a Apple apresentou seu Estrutura MultipeerConnectivity (doravante MPC), projetado para permitir que os aplicativos executem essas tarefas com um esforço relativamente baixo.
MPC cuida de grande parte da infraestrutura necessária subjacente aqui:
Neste artigo, abordaremos principalmente a implementação do iOS, mas a maioria, senão tudo, é aplicável ao macOS e tvOS.
Ciclo de vida da sessão multipeer:
MCNearbyServiceAdvertiser.startAdvertisingForPeers()
MCNearbyServiceBrowser.startBrowsingForPeers()
MCNearbyServiceBrowserDelegate.browser(:foundPeer:withDiscoveryInfo:)
MCNearbyServiceBrowser.invitePeer(...)
MCNearbyServiceAdvertiserDelegate.didReceiveInvitationFromPeer(...)
invitationHandler
em didReceiveInvitation
Create the MCSession
MCSession.send(...)
MCSessionDelegate.session(_:didReceive:data:peerID)
MCSession.disconnect()
Consulte esta imagem de vez em quando
Existem inúmeros tutoriais e exemplos do MultipeerConnectivity por aí que pretendem caminhar desenvolvedores iOS por meio da implementação de um aplicativo baseado em MPC. No entanto, na minha experiência, eles geralmente são incompletos e tendem a encobrir alguns obstáculos potenciais importantes com o MPC. Neste artigo, espero guiar o leitor por uma implementação rudimentar de tal aplicativo e apontar áreas onde eu achei fácil travar.
MPC é baseado em um punhado de classes. Vamos examinar a lista dos mais comuns e aumentar nossa compreensão da estrutura.
MCSession
- Uma sessão gerencia todas as comunicações entre seus pares associados. Você pode enviar mensagens, arquivos e fluxos por meio de uma sessão, e seu delegado será notificado quando um deles for recebido de um par conectado.MCPeerID
- Um ID de par permite que você identifique dispositivos de par individuais em uma sessão. Tem um nome associado a ele, mas tenha cuidado: IDs de mesmo nome não são considerados idênticos (consulte Regras básicas, abaixo).MCNearbyServiceAdvertiser
- Um anunciante permite que você transmita o nome do seu serviço para dispositivos próximos. Isso permite que eles se conectem a você.MCNearbyServiceBrowser
- Um navegador permite pesquisar dispositivos usando MCNearbyServiceAdvertiser
. Usar essas duas classes juntas permite que você descubra dispositivos próximos e crie suas conexões ponto a ponto.MCBrowserViewController
- Isso fornece uma IU muito básica para navegar pelos serviços do dispositivo próximo (vendido via MCNearbyServiceAdvertiser
). Embora seja adequado para alguns casos de uso, não o usaremos, pois, em minha experiência, um dos melhores aspectos do MCP é sua perfeição.Há algumas coisas a se ter em mente ao construir uma rede MPC:
Antes de estabelecermos nossa rede, precisamos fazer algumas tarefas domésticas e, em seguida, configurar as classes de anunciante e navegador para descobrir outros dispositivos com os quais podemos nos comunicar. Vamos criar um singleton que usaremos para manter algumas variáveis de estado (nosso MCPeerID local e quaisquer dispositivos conectados), então criaremos MCNearbyServiceAdvertiser
e MCNearbyServiceBrowser
. Esses dois últimos objetos precisam de um tipo de serviço, que é apenas uma string que identifica seu aplicativo. Deve ter menos de 16 caracteres e deve ser o mais exclusivo possível (ou seja, “MyApp-MyCo”, não “Multipeer”). Podemos especificar um (pequeno) dicionário para nosso anunciante do que os navegadores podem ler para fornecer um pouco mais de informação ao olhar para dispositivos próximos (talvez um tipo de jogo ou função do dispositivo).
Como o MPC depende de APIs fornecidas pelo sistema e se correlaciona com objetos do mundo real (outros dispositivos, bem como a 'rede' compartilhada entre eles), é um bom ajuste para o padrão singleton. Embora freqüentemente usados em excesso, os singletons são uma boa opção para recursos compartilhados como este.
Aqui está a definição do nosso singleton:
class MPCManager: NSObject { var advertiser: MCNearbyServiceAdvertiser! var browser: MCNearbyServiceBrowser! static let instance = MPCManager() let localPeerID: MCPeerID let serviceType = 'MPC-Testing' var devices: [Device] = [] override init() { if let data = UserDefaults.standard.data(forKey: 'peerID'), let id = NSKeyedUnarchiver.unarchiveObject(with: data) as? MCPeerID { self.localPeerID = id } else { let peerID = MCPeerID(displayName: UIDevice.current.name) let data = try? NSKeyedArchiver.archivedData(withRootObject: peerID) UserDefaults.standard.set(data, forKey: 'peerID') self.localPeerID = peerID } super.init() self.advertiser = MCNearbyServiceAdvertiser(peer: localPeerID, discoveryInfo: nil, serviceType: self.serviceType) self.advertiser.delegate = self self.browser = MCNearbyServiceBrowser(peer: localPeerID, serviceType: self.serviceType) self.browser.delegate = self } }
Observe que estamos armazenando nosso MCPeerID
nos padrões do usuário (por meio de NSKeyedArchiver
) e reutilizá-lo. Como mencionado acima, isso é importante, e deixar de armazená-lo em cache de alguma forma pode causar bugs obscuros mais adiante na linha.
Esta é nossa classe de dispositivos, que usaremos para rastrear quais dispositivos foram descobertos e qual é seu estado:
class Device: NSObject { let peerID: MCPeerID var session: MCSession? var name: String var state = MCSessionState.notConnected init(peerID: MCPeerID) { self.name = peerID.displayName self.peerID = peerID super.init() } func invite() { browser.invitePeer(self.peerID, to: self.session!, withContext: nil, timeout: 10) } }
Agora que construímos nossas aulas iniciais, é hora de voltar atrás e pensar sobre a interação entre navegadores e anunciantes. No MPC, um dispositivo pode anunciar um serviço que oferece e pode procurar um serviço de seu interesse em outros dispositivos. Como estamos focados na comunicação de dispositivo a dispositivo usando apenas nosso aplicativo, vamos anunciar e procurar o mesmo serviço.
Em uma configuração cliente / servidor tradicional, um dispositivo (o servidor) anunciaria seus serviços e o cliente os procuraria. Como somos igualitários, não queremos ter que especificar funções para nossos dispositivos; teremos todos os dispositivos para anunciar e navegar.
Precisamos adicionar um método ao nosso MPCManager
para criar dispositivos conforme eles são descobertos e rastreá-los em nossa gama de dispositivos. Nosso método pegará um MCPeerID
, procurará por um dispositivo existente com aquele ID e o retornará se encontrado. Se ainda não temos um dispositivo existente, criamos um novo e o adicionamos ao nosso conjunto de dispositivos.
func device(for id: MCPeerID) -> Device { for device in self.devices { if device.peerID == id { return device } } let device = Device(peerID: id) self.devices.append(device) return device }
Depois que um dispositivo começa a anunciar, outro dispositivo de navegação pode tentar se conectar a ele. Precisamos adicionar métodos delegados ao nosso MPCSession
classe para lidar com chamadas de delegado recebidas de nosso anunciante neste caso:
extension MPCManager: MCNearbyServiceAdvertiserDelegate { func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) { let device = MPCManager.instance.device(for: peerID) device.connect() invitationHandler(true, device.session) } }
… Um método em nosso dispositivo para criar a MCSession:
func connect() { if self.session != nil { return } self.session = MCSession(peer: MPCManager.instance.localPeerID, securityIdentity: nil, encryptionPreference: .required) self.session?.delegate = self }
… E, finalmente, um método para acionar o convite quando nosso navegador descobrir um anunciante:
extension MPCManager: MCNearbyServiceBrowserDelegate { func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) { let device = MPCManager.instance.device(for: peerID) device.invite(with: self.browser) }
No momento, estamos ignorando o withDiscoveryInfo
argumento; poderíamos usar isso para filtrar dispositivos específicos com base no que eles disponibilizaram (este é o mesmo dicionário que fornecemos no discoveryInfo
argumento para MCNearbyServiceAdvertiser
, acima).
Agora que cuidamos de todas as nossas tarefas domésticas, podemos começar o negócio real de conectar dispositivos.
No método init de nossa MPCSession, configuramos nosso anunciante e nosso delegado. Quando estivermos prontos para iniciar a conexão, precisaremos iniciar os dois. Isso pode ser feito no método didFinishLaunching do delegado do aplicativo ou sempre que for apropriado. Aqui está o start()
método que adicionaremos à nossa classe:
func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() }
Essas chamadas significarão que seu aplicativo começará a transmitir sua presença por WiFi. Observe que você não precisa estar conectado a uma rede WiFi para que isso funcione (mas você precisa tê-lo ligado).
Quando um dispositivo responde a um convite e inicia sua MCSession, ele começa a receber callbacks delegados da sessão. Vamos adicionar manipuladores para aqueles em nosso objeto de dispositivo; a maioria deles vamos ignorar por enquanto:
extension Device: MCSessionDelegate { public func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { self.state = state NotificationCenter.default.post(name: Multipeer.Notifications.deviceDidChangeState, object: self) } public func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { } public func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { } public func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { } }
Por enquanto, estamos principalmente preocupados com session(_:peer:didChangeState:)
ligue de volta. Será chamado sempre que um dispositivo fizer a transição para um novo estado (notConnected
, connecting
e connected
). Queremos acompanhar isso para que possamos construir uma lista de todos os dispositivos conectados:
extension MPCManager { var connectedDevices: [Device] { return self.devices.filter { $0.state == .connected } } }
Agora que todos os nossos dispositivos estão conectados, é hora de realmente começar a enviar mensagens para frente e para trás. MPC oferece três opções a este respeito:
Por uma questão de simplicidade, examinaremos apenas a primeira dessas opções. Enviaremos mensagens simples de um lado para outro e não nos preocuparemos muito com as complexidades dos tipos de mensagem, formatação, etc. Usaremos uma estrutura Codable para encapsular nossa mensagem, que será semelhante a esta:
struct Message: Codable { let body: String }
Também adicionaremos uma extensão ao Dispositivo para enviar um destes:
extension Device { func send(text: String) throws { let message = Message(body: text) let payload = try JSONEncoder().encode(message) try self.session?.send(payload, toPeers: [self.peerID], with: .reliable) } } ~~~swift Finally, we'll need to modify our `Device.session(_:didReceive:fromPeer)` code to receive the message, parse it, and notify any interested objects about it:
static let messageReceivedNotification = Notification.Name (“DeviceDidReceiveMessage”) sessão função pública (_ sessão: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {if let message = try? JSONDecoder (). Decode (Message.self, from: data) {NotificationCenter.default.post (name: Device.messageReceivedNotification, object: message, userInfo: [“from”: self])}}
## Disconnections Now that we've got a connection created between multiple devices, we have to be able to both disconnect on demand and also handle system interruptions. One of the undocumented weaknesses of MPC is that it doesn't function in the background. We need to observe the `UIApplication.didEnterBackgroundNotification` notification, and make sure that we shut down all our sessions. Failure to do this will lead to undefined states in the sessions and devices and can cause lots of confusing, hard-to-track-down errors. There is a temptation to use a background task to keep your sessions around, in case the user jumps back into your app. However, this is a bad idea, as MPC will usually fail within the first second of being backgrounded. When your app returns to the foreground, you can rely on MPC's delegate methods to rebuild your connections. In our MPCSession's `start()` method, we'll want to observe this notification and add code to handle it and shut down all our sessions. ~~~swift func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: Notification.Name.UIApplicationDidEnterBackground, object: nil) } @objc func enteredBackground() { for device in self.devices { device.disconnect() } } func disconnect() { self.session?.disconnect() self.session = nil }
Este artigo cobre a arquitetura necessária para construir os componentes de rede de um aplicativo baseado em MultipeerConnectivity. O código-fonte completo (disponível no Github) oferece um invólucro de interface de usuário mínimo que permite visualizar os dispositivos conectados e enviar mensagens entre eles.
MPC oferece conectividade quase perfeita entre dispositivos próximos sem a necessidade de se preocupar com redes WiFi, Bluetooth ou ginástica complexa de cliente / servidor. Ser capaz de emparelhar rapidamente alguns telefones para uma curta sessão de jogo ou conectar dois dispositivos para compartilhamento é feito da maneira típica da Apple.
O código-fonte deste projeto está disponível no Github em https://github.com/bengottlieb/MultipeerExample .
Projetando um iOS que usa AFNetworking? O padrão de design Model-View-Controller (MVC) é ótimo para uma base de código de manutenção, mas às vezes você precisa de uma única classe para lidar com sua rede devido a questões como código DRY, registro de rede centralizado e, especialmente, limitação de taxa. Leia tudo sobre como lidar com isso com uma classe Singleton em Rede centralizada e desacoplada do iOS: Tutorial AFNetworking com uma classe singleton
Um aplicativo em rede capaz de se conectar a outras instâncias (pares) sem precisar de um servidor ou outro intermediário.
Se seus dados são confidenciais (como arquivos compartilhados), você pode querer evitar que outras pessoas os interceptem. Para muitos aplicativos ponto a ponto, no entanto, não é necessário (pense: jogadas de jogo ou instruções simples).
Quando dois dispositivos estão conectados, eles se comunicam por meio de mensagens enviadas e recebidas.
Em uma rede cliente-servidor tradicional, a infraestrutura inclui o servidor e o equipamento de conexão (por exemplo, uma estação base WiFi). Em um modelo ponto a ponto, isso pode ser reduzido a uma rede que consiste apenas nos próprios dispositivos e uma conexão ad-hoc (sem hardware externo, apenas os próprios rádios dos dispositivos).
A comunicação ponto a ponto envolve dispositivos em uma rede que se comunicam diretamente entre si, sem passar por um servidor intermediário.
As redes ponto a ponto podem ser configuradas em qualquer lugar onde haja dois ou mais dispositivos capazes, sem a necessidade de envolver um terceiro servidor, potencialmente inacessível. Por exemplo, quando não há conexão com a internet, mas os dispositivos ainda podem se ver.
O MPC cuida de muitas das tarefas básicas de configuração e manutenção, como descoberta de pares e comunicações de baixo nível. Isso economiza muito tempo e esforço do desenvolvedor ao lidar com funções comuns para eles.
Existem alguns pontos fracos não bem documentados que, se não forem protegidos, podem causar dificuldades de conexão. Também há uma perda geral de flexibilidade que vem com uma estrutura de uso geral. Para a maioria dos usos, entretanto, o MPC é adequado.
Agora que todos os nossos dispositivos estão conectados, é hora de realmente começar a enviar mensagens para frente e para trás. MPC oferece três opções a este respeito:
Por uma questão de simplicidade, examinaremos apenas a primeira dessas opções. Enviaremos mensagens simples de um lado para outro e não nos preocuparemos muito com as complexidades dos tipos de mensagem, formatação, etc. Usaremos uma estrutura Codable para encapsular nossa mensagem, que será semelhante a esta:
struct Message: Codable { let body: String }
Também adicionaremos uma extensão ao Dispositivo para enviar um destes:
extension Device { func send(text: String) throws { let message = Message(body: text) let payload = try JSONEncoder().encode(message) try self.session?.send(payload, toPeers: [self.peerID], with: .reliable) } } ~~~swift Finally, we'll need to modify our `Device.session(_:didReceive:fromPeer)` code to receive the message, parse it, and notify any interested objects about it:
static let messageReceivedNotification = Notification.Name (“DeviceDidReceiveMessage”) sessão função pública (_ sessão: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {if let message = try? JSONDecoder (). Decode (Message.self, from: data) {NotificationCenter.default.post (name: Device.messageReceivedNotification, object: message, userInfo: [“from”: self])}}
## Disconnections Now that we've got a connection created between multiple devices, we have to be able to both disconnect on demand and also handle system interruptions. One of the undocumented weaknesses of MPC is that it doesn't function in the background. We need to observe the `UIApplication.didEnterBackgroundNotification` notification, and make sure that we shut down all our sessions. Failure to do this will lead to undefined states in the sessions and devices and can cause lots of confusing, hard-to-track-down errors. There is a temptation to use a background task to keep your sessions around, in case the user jumps back into your app. However, this is a bad idea, as MPC will usually fail within the first second of being backgrounded. When your app returns to the foreground, you can rely on MPC's delegate methods to rebuild your connections. In our MPCSession's `start()` method, we'll want to observe this notification and add code to handle it and shut down all our sessions. ~~~swift func start() { self.advertiser.startAdvertisingPeer() self.browser.startBrowsingForPeers() NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: Notification.Name.UIApplicationDidEnterBackground, object: nil) } @objc func enteredBackground() { for device in self.devices { device.disconnect() } } func disconnect() { self.session?.disconnect() self.session = nil }
Este artigo cobre a arquitetura necessária para construir os componentes de rede de um aplicativo baseado em MultipeerConnectivity. O código-fonte completo (disponível no Github) oferece um invólucro de interface de usuário mínimo que permite visualizar os dispositivos conectados e enviar mensagens entre eles.
MPC oferece conectividade quase perfeita entre dispositivos próximos sem a necessidade de se preocupar com redes WiFi, Bluetooth ou ginástica complexa de cliente / servidor. Ser capaz de emparelhar rapidamente alguns telefones para uma curta sessão de jogo ou conectar dois dispositivos para compartilhamento é feito da maneira típica da Apple.
O código-fonte deste projeto está disponível no Github em https://github.com/bengottlieb/MultipeerExample .
Projetando um iOS que usa AFNetworking? O padrão de design Model-View-Controller (MVC) é ótimo para uma base de código de manutenção, mas às vezes você precisa de uma única classe para lidar com sua rede devido a questões como código DRY, registro de rede centralizado e, especialmente, limitação de taxa. Leia tudo sobre como lidar com isso com uma classe Singleton em Rede centralizada e desacoplada do iOS: Tutorial AFNetworking com uma classe singleton
Um aplicativo em rede capaz de se conectar a outras instâncias (pares) sem precisar de um servidor ou outro intermediário.
Se seus dados são confidenciais (como arquivos compartilhados), você pode querer evitar que outras pessoas os interceptem. Para muitos aplicativos ponto a ponto, no entanto, não é necessário (pense: jogadas de jogo ou instruções simples).
Quando dois dispositivos estão conectados, eles se comunicam por meio de mensagens enviadas e recebidas.
Em uma rede cliente-servidor tradicional, a infraestrutura inclui o servidor e o equipamento de conexão (por exemplo, uma estação base WiFi). Em um modelo ponto a ponto, isso pode ser reduzido a uma rede que consiste apenas nos próprios dispositivos e uma conexão ad-hoc (sem hardware externo, apenas os próprios rádios dos dispositivos).
A comunicação ponto a ponto envolve dispositivos em uma rede que se comunicam diretamente entre si, sem passar por um servidor intermediário.
As redes ponto a ponto podem ser configuradas em qualquer lugar onde haja dois ou mais dispositivos capazes, sem a necessidade de envolver um terceiro servidor, potencialmente inacessível. Por exemplo, quando não há conexão com a internet, mas os dispositivos ainda podem se ver.
O MPC cuida de muitas das tarefas básicas de configuração e manutenção, como descoberta de pares e comunicações de baixo nível. Isso economiza muito tempo e esforço do desenvolvedor ao lidar com funções comuns para eles.
Existem alguns pontos fracos não bem documentados que, se não forem protegidos, podem causar dificuldades de conexão. Também há uma perda geral de flexibilidade que vem com uma estrutura de uso geral. Para a maioria dos usos, entretanto, o MPC é adequado.