SwApp para Compartir Datos
TODO: collapasable, copyable, download, etc
Can you output a SwiftUI app that uses the Multipeer Connectivity Framework to allow to iOS applications to connect? In order to test this, we will display some basic accelarameter information for the other connected device.Sure, here is a simple SwiftUI app that uses the Multipeer Connectivity framework to connect two iOS devices and share accelerometer data between them. This example includes basic accelerometer data sharing and display.
1. Create a new SwiftUI project
Open Xcode and create a new SwiftUI project.
2. Add necessary frameworks
In your Info.plist
, add the required keys for accessing the accelerometer:
<key>NSMotionUsageDescription</key>
<string>We need access to your accelerometer to share data with connected devices.</string>
3. Create the Multipeer Connectivity Manager
Create a new Swift file named MultipeerManager.swift
:
import Foundation
import MultipeerConnectivity
class MultipeerManager: NSObject, ObservableObject {
private let serviceType = "accel-share"
private let myPeerId = MCPeerID(displayName: UIDevice.current.name)
private let serviceAdvertiser: MCNearbyServiceAdvertiser
private let serviceBrowser: MCNearbyServiceBrowser
@Published var receivedData: String = ""
override init() {
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerId, discoveryInfo: nil, serviceType: serviceType)
self.serviceBrowser = MCNearbyServiceBrowser(peer: myPeerId, serviceType: serviceType)
super.init()
self.serviceAdvertiser.delegate = self
self.serviceBrowser.delegate = self
}
func start() {
serviceAdvertiser.startAdvertisingPeer()
serviceBrowser.startBrowsingForPeers()
}
func stop() {
serviceAdvertiser.stopAdvertisingPeer()
serviceBrowser.stopBrowsingForPeers()
}
var session: MCSession = MCSession(peer: MCPeerID(displayName: UIDevice.current.name), securityIdentity: nil, encryptionPreference: .required)
func send(data: Data) {
if !session.connectedPeers.isEmpty {
do {
try session.send(data, toPeers: session.connectedPeers, with: .reliable)
} catch let error {
print("Error sending data: \(error.localizedDescription)")
}
}
}
}
extension MultipeerManager: MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate {
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
invitationHandler(true, self.session)
}
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
browser.invitePeer(peerID, to: self.session, withContext: nil, timeout: 10)
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
// Handle lost peer
}
}
extension MultipeerManager: MCSessionDelegate {
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
// Handle peer connection state changes
}
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
DispatchQueue.main.async {
self.receivedData = String(data: data, encoding: .utf8) ?? ""
}
}
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {}
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {}
}
4. Create the MotionManager
Create a new Swift file named MotionManager.swift
:
import Foundation
import CoreMotion
class MotionManager: ObservableObject {
private var motionManager: CMMotionManager
@Published var accelerometerData: String = ""
init() {
self.motionManager = CMMotionManager()
self.motionManager.accelerometerUpdateInterval = 0.1
}
func startUpdates() {
if self.motionManager.isAccelerometerAvailable {
self.motionManager.startAccelerometerUpdates(to: .main) { data, error in
guard let data = data else { return }
self.accelerometerData = "x: \(data.acceleration.x), y: \(data.acceleration.y), z: \(data.acceleration.z)"
}
}
}
func stopUpdates() {
self.motionManager.stopAccelerometerUpdates()
}
}
5. Create the ContentView
Update ContentView.swift
to use MultipeerManager
and MotionManager
:
import SwiftUI
struct ContentView: View {
@ObservedObject var multipeerManager = MultipeerManager()
@ObservedObject var motionManager = MotionManager()
var body: some View {
VStack {
Text("My Accelerometer Data")
.font(.headline)
Text(motionManager.accelerometerData)
.padding()
Text("Received Accelerometer Data")
.font(.headline)
Text(multipeerManager.receivedData)
.padding()
}
.onAppear {
self.motionManager.startUpdates()
self.multipeerManager.start()
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
let data = self.motionManager.accelerometerData.data(using: .utf8)
self.multipeerManager.send(data: data!)
}
}
.onDisappear {
self.motionManager.stopUpdates()
self.multipeerManager.stop()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
6. Run the app on two devices
To test this app, run it on two iOS devices. The accelerometer data from each device will be sent to the other and displayed on the screen.
Summary
This basic example shows how to set up Multipeer Connectivity to share accelerometer data between two devices. You can expand on this to handle more complex data or interactions as needed.Unsupported multimodal_textThis error typically occurs when there are multiple Info.plist
files in your project or when the Info.plist
file is included in the build phase multiple times. Here's how to resolve this issue:
Check Build Phases:
- Go to your project settings in Xcode.
- Select your target.
- Navigate to the "Build Phases" tab.
- Look for the "Copy Bundle Resources" section.
- Ensure that there is only one
Info.plist
file listed here. If there are duplicates, remove the extra entries.
Check File Locations:
- Ensure that there is only one
Info.plist
file in your project directory structure.
- Ensure that there is only one
Remove and Re-add the Info.plist:
- Remove the
Info.plist
file from the project navigator. - Delete any duplicate
Info.plist
files if they exist in the project directory. - Re-add the
Info.plist
file to your project.
- Remove the
Here are the detailed steps:
1. Check Build Phases
- Open your project in Xcode.
- Select your target from the list of targets in the project navigator.
- Click on the "Build Phases" tab.
- Expand the "Copy Bundle Resources" section.
- Look for multiple entries of
Info.plist
. If you see more than one, remove the duplicates by clicking the minus (-) button next to them.
2. Check File Locations
- In the Finder, navigate to your project directory.
- Check if there are multiple
Info.plist
files in your project directory. If there are, remove the unnecessary ones.
3. Remove and Re-add the Info.plist
- In Xcode, select the
Info.plist
file in the project navigator and press the delete key. - Choose "Remove References" (not "Move to Trash").
- In the Finder, ensure there is only one
Info.plist
file in your project directory. - Drag and drop the
Info.plist
file back into the Xcode project navigator.
After performing these steps, try building your project again. This should resolve the issue. If you still encounter the error, please let me know.Unsupported multimodal_textThe errors you're encountering, with NSNetServicesErrorCode = "-72008"
and NSNetServicesErrorDomain = 10
, are related to the NSNetService
class in iOS, which is used for network services discovery and publication. These errors usually indicate an issue with the network configuration or permissions.
Here are some common causes and solutions for these errors:
1. Network Permissions
Make sure your app has the appropriate permissions to use network services. Since iOS 14, you need to declare and request local network privacy permissions.
Add the following to your Info.plist
:
<key>NSLocalNetworkUsageDescription</key>
<string>We need access to the local network to connect with other devices.</string>
<key>NSBonjourServices</key>
<array>
<string>_accel-share._tcp</string>
</array>
2. Network Configuration
Ensure your network settings allow Bonjour services and that your devices are on the same network.
3. Code Review
Double-check your MultipeerManager
code for any misconfigurations. Here is a review of the critical parts:
Advertiser Configuration
Make sure the serviceType
you are using is correct and matches between the advertiser and the browser.
Example Code Adjustment
Here's a concise check to ensure everything is set up correctly:
import Foundation
import MultipeerConnectivity
class MultipeerManager: NSObject, ObservableObject {
private let serviceType = "accel-share"
private let myPeerId = MCPeerID(displayName: UIDevice.current.name)
private let serviceAdvertiser: MCNearbyServiceAdvertiser
private let serviceBrowser: MCNearbyServiceBrowser
@Published var receivedData: String = ""
override init() {
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerId, discoveryInfo: nil, serviceType: serviceType)
self.serviceBrowser = MCNearbyServiceBrowser(peer: myPeerId, serviceType: serviceType)
super.init()
self.serviceAdvertiser.delegate = self
self.serviceBrowser.delegate = self
self.session.delegate = self // Ensure session delegate is set
}
func start() {
serviceAdvertiser.startAdvertisingPeer()
serviceBrowser.startBrowsingForPeers()
}
func stop() {
serviceAdvertiser.stopAdvertisingPeer()
serviceBrowser.stopBrowsingForPeers()
}
var session: MCSession = MCSession(peer: MCPeerID(displayName: UIDevice.current.name), securityIdentity: nil, encryptionPreference: .required)
func send(data: Data) {
if !session.connectedPeers.isEmpty {
do {
try session.send(data, toPeers: session.connectedPeers, with: .reliable)
} catch let error {
print("Error sending data: \(error.localizedDescription)")
}
}
}
}
extension MultipeerManager: MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate {
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
invitationHandler(true, self.session)
}
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
browser.invitePeer(peerID, to: self.session, withContext: nil, timeout: 10)
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
// Handle lost peer
}
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
// Handle peer connection state changes
}
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
DispatchQueue.main.async {
self.receivedData = String(data: data, encoding: .utf8) ?? ""
}
}
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {}
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {}
}
4. Check Bonjour Service Configuration
Ensure that the _accel-share._tcp
service type is correctly set up and that it matches the service type used in the MultipeerManager
.
By following these steps, you should be able to resolve the errors and successfully run your app. If the problem persists, double-check your network environment to ensure that Bonjour services are not being blocked.Unsupported multimodal_textThe errors you are encountering, such as Wrong connection data. Participant ID from remote connection data = ...
, indicate that there is a mismatch or conflict in the session data between the peers. This usually happens due to misconfiguration or issues in the connection establishment phase.
Here are some steps to help diagnose and resolve these issues:
1. Ensure Unique Peer IDs
Make sure each device has a unique MCPeerID
. Using the device name as the display name for MCPeerID
can help ensure uniqueness.
2. Check Session State
Ensure that both devices are in a proper state to establish a connection. Make sure the session's delegate methods are correctly implemented to handle state changes and errors.
3. Review Code Implementation
Here is a more refined implementation of the MultipeerManager
with additional logging to help diagnose issues:
import Foundation
import MultipeerConnectivity
class MultipeerManager: NSObject, ObservableObject {
private let serviceType = "accel-share"
private let myPeerId = MCPeerID(displayName: UIDevice.current.name)
private let serviceAdvertiser: MCNearbyServiceAdvertiser
private let serviceBrowser: MCNearbyServiceBrowser
@Published var receivedData: String = ""
override init() {
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerId, discoveryInfo: nil, serviceType: serviceType)
self.serviceBrowser = MCNearbyServiceBrowser(peer: myPeerId, serviceType: serviceType)
super.init()
self.serviceAdvertiser.delegate = self
self.serviceBrowser.delegate = self
self.session.delegate = self // Ensure session delegate is set
}
func start() {
serviceAdvertiser.startAdvertisingPeer()
serviceBrowser.startBrowsingForPeers()
}
func stop() {
serviceAdvertiser.stopAdvertisingPeer()
serviceBrowser.stopBrowsingForPeers()
}
var session: MCSession = MCSession(peer: MCPeerID(displayName: UIDevice.current.name), securityIdentity: nil, encryptionPreference: .required)
func send(data: Data) {
if !session.connectedPeers.isEmpty {
do {
try session.send(data, toPeers: session.connectedPeers, with: .reliable)
} catch let error {
print("Error sending data: \(error.localizedDescription)")
}
}
}
}
extension MultipeerManager: MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate {
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
print("Received invitation from \(peerID.displayName)")
invitationHandler(true, self.session)
}
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
print("Found peer: \(peerID.displayName)")
browser.invitePeer(peerID, to: self.session, withContext: nil, timeout: 10)
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
print("Lost peer: \(peerID.displayName)")
}
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
switch state {
case .connected:
print("Connected to peer: \(peerID.displayName)")
case .connecting:
print("Connecting to peer: \(peerID.displayName)")
case .notConnected:
print("Not connected to peer: \(peerID.displayName)")
@unknown default:
fatalError()
}
}
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
DispatchQueue.main.async {
self.receivedData = String(data: data, encoding: .utf8) ?? ""
}
}
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {}
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {}
}
4. Ensure Both Devices Are Discoverable
Make sure both devices are advertising and browsing for peers simultaneously to ensure they can discover each other.
5. Network Environment
Ensure that both devices are on the same network and that there are no network restrictions or firewalls blocking the connection.
6. Additional Debugging
Add more logging to track the states and data being sent/received. This can help identify where the connection is failing or what data might be causing issues.
By following these steps and ensuring your implementation is correct, you should be able to resolve the connection issues between your devices. If the problem persists, you may want to verify your network environment or look for any underlying issues with the Multipeer Connectivity framework in your specific iOS version.The Simulator doesn't ask me for permission to use the network, is that expected?When running your app on the iOS Simulator, it should typically ask for permissions just like it would on a real device. If you are not being prompted for network permissions, there might be a few reasons why this is happening. Here are some steps you can take to troubleshoot and ensure that permissions are requested correctly:
1. Verify Info.plist
Configuration
Make sure your Info.plist
is correctly configured to request local network permissions. You should have the following keys in your Info.plist
:
<key>NSLocalNetworkUsageDescription</key>
<string>We need access to the local network to connect with other devices.</string>
<key>NSBonjourServices</key>
<array>
<string>_accel-share._tcp</string>
</array>
2. Reset Location and Privacy Settings in the Simulator
Sometimes the Simulator's privacy settings can get out of sync. Resetting them can help:
- Open the iOS Simulator.
- Go to the menu
Device
->Erase All Content and Settings...
. - Confirm to reset the Simulator.
This will reset all permissions and settings, so the next time your app runs, it should request permissions again.
3. Check Privacy Settings in the Simulator
Ensure that the app is allowed to access the local network:
- Open the Settings app on the iOS Simulator.
- Navigate to
Privacy
->Local Network
. - Check if your app is listed and ensure it has permission to access the local network.
4. Ensure the App Requests Permission at Runtime
Double-check your code to ensure that it triggers the network access that would prompt for permissions. The following code snippet shows how to access the network in a way that should trigger the permission prompt:
import Foundation
import MultipeerConnectivity
class MultipeerManager: NSObject, ObservableObject {
private let serviceType = "accel-share"
private let myPeerId = MCPeerID(displayName: UIDevice.current.name)
private let serviceAdvertiser: MCNearbyServiceAdvertiser
private let serviceBrowser: MCNearbyServiceBrowser
@Published var receivedData: String = ""
override init() {
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerId, discoveryInfo: nil, serviceType: serviceType)
self.serviceBrowser = MCNearbyServiceBrowser(peer: myPeerId, serviceType: serviceType)
super.init()
self.serviceAdvertiser.delegate = self
self.serviceBrowser.delegate = self
self.session.delegate = self // Ensure session delegate is set
}
func start() {
serviceAdvertiser.startAdvertisingPeer()
serviceBrowser.startBrowsingForPeers()
}
func stop() {
serviceAdvertiser.stopAdvertisingPeer()
serviceBrowser.stopBrowsingForPeers()
}
var session: MCSession = MCSession(peer: MCPeerID(displayName: UIDevice.current.name), securityIdentity: nil, encryptionPreference: .required)
func send(data: Data) {
if !session.connectedPeers.isEmpty {
do {
try session.send(data, toPeers: session.connectedPeers, with: .reliable)
} catch let error {
print("Error sending data: \(error.localizedDescription)")
}
}
}
}
extension MultipeerManager: MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate {
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
print("Received invitation from \(peerID.displayName)")
invitationHandler(true, self.session)
}
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
print("Found peer: \(peerID.displayName)")
browser.invitePeer(peerID, to: self.session, withContext: nil, timeout: 10)
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
print("Lost peer: \(peerID.displayName)")
}
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
switch state {
case .connected:
print("Connected to peer: \(peerID.displayName)")
case .connecting:
print("Connecting to peer: \(peerID.displayName)")
case .notConnected:
print("Not connected to peer: \(peerID.displayName)")
@unknown default:
fatalError()
}
}
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
DispatchQueue.main.async {
self.receivedData = String(data: data, encoding: .utf8) ?? ""
}
}
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {}
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {}
}
5. Verify App Build and Run
Ensure you are running the correct build of your app:
- Clean the build folder in Xcode (
Shift + Command + K
). - Rebuild the app (
Command + B
). - Run the app on the Simulator.
By following these steps, your app should request the necessary network permissions. If you continue to experience issues, it might be helpful to run the app on a physical device to see if the behavior is consistent.Unsupported multimodal_textThe logs indicate that the connection is being attempted but failing due to mismatched connection data or incorrect session state. Here are some steps to diagnose and resolve these issues:
1. Ensure Unique MCPeerID
Make sure that each device has a unique MCPeerID
. The MCPeerID
should be based on the device name or some other unique identifier.
2. Check Session State
Make sure the session state is correctly managed and logged. This will help identify if there are any issues during the connection phase.
3. Update MultipeerManager
Code
Here's an updated version of the MultipeerManager
with enhanced logging to help debug the issue:
import Foundation
import MultipeerConnectivity
class MultipeerManager: NSObject, ObservableObject {
private let serviceType = "accel-share"
private let myPeerId = MCPeerID(displayName: UIDevice.current.name)
private let serviceAdvertiser: MCNearbyServiceAdvertiser
private let serviceBrowser: MCNearbyServiceBrowser
@Published var receivedData: String = ""
override init() {
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: myPeerId, discoveryInfo: nil, serviceType: serviceType)
self.serviceBrowser = MCNearbyServiceBrowser(peer: myPeerId, serviceType: serviceType)
super.init()
self.serviceAdvertiser.delegate = self
self.serviceBrowser.delegate = self
self.session.delegate = self // Ensure session delegate is set
print("Initialized MultipeerManager with peerID: \(myPeerId.displayName)")
}
func start() {
print("Starting advertiser and browser...")
serviceAdvertiser.startAdvertisingPeer()
serviceBrowser.startBrowsingForPeers()
}
func stop() {
print("Stopping advertiser and browser...")
serviceAdvertiser.stopAdvertisingPeer()
serviceBrowser.stopBrowsingForPeers()
}
var session: MCSession = MCSession(peer: MCPeerID(displayName: UIDevice.current.name), securityIdentity: nil, encryptionPreference: .required)
func send(data: Data) {
if !session.connectedPeers.isEmpty {
do {
try session.send(data, toPeers: session.connectedPeers, with: .reliable)
print("Sent data: \(String(data: data, encoding: .utf8) ?? "")")
} catch let error {
print("Error sending data: \(error.localizedDescription)")
}
} else {
print("No connected peers to send data to.")
}
}
}
extension MultipeerManager: MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate {
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
print("Received invitation from \(peerID.displayName)")
invitationHandler(true, self.session)
}
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
print("Found peer: \(peerID.displayName)")
browser.invitePeer(peerID, to: self.session, withContext: nil, timeout: 10)
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
print("Lost peer: \(peerID.displayName)")
}
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
switch state {
case .connected:
print("Connected to peer: \(peerID.displayName)")
case .connecting:
print("Connecting to peer: \(peerID.displayName)")
case .notConnected:
print("Not connected to peer: \(peerID.displayName)")
@unknown default:
fatalError()
}
}
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
DispatchQueue.main.async {
self.receivedData = String(data: data, encoding: .utf8) ?? ""
print("Received data: \(self.receivedData) from \(peerID.displayName)")
}
}
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {}
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {}
}
4. Verify Networking Permissions
Ensure your app has the necessary permissions for local network access:
<key>NSLocalNetworkUsageDescription</key>
<string>We need access to the local network to connect with other devices.</string>
<key>NSBonjourServices</key>
<array>
<string>_accel-share._tcp</string>
</array>
5. Test on Real Devices
Testing on real devices is crucial as the Simulator might not behave exactly like a real device, especially with network permissions and connectivity.
6. Additional Debugging
Add more logging to track the states and data being sent/received. This can help identify where the connection is failing or what data might be causing issues.
By following these steps, you should be able to identify and resolve the connection issues between your devices. If the problem persists, you may want to verify your network environment or look for any underlying issues with the Multipeer Connectivity framework in your specific iOS version.The main problem with your code, was that you instantiated MCPeerID twice... so it wasn't stable. I fixed it.Thank you for pointing that out. Ensuring that MCPeerID
is instantiated once and reused is critical for maintaining a stable peer identity throughout the app's lifecycle. Here’s the corrected and optimized version of the MultipeerManager
to ensure the stability of MCPeerID
:
MultipeerManager.swift
import Foundation
import MultipeerConnectivity
class MultipeerManager: NSObject, ObservableObject {
private let serviceType = "accel-share"
private let myPeerId: MCPeerID
private let serviceAdvertiser: MCNearbyServiceAdvertiser
private let serviceBrowser: MCNearbyServiceBrowser
@Published var receivedData: String = ""
private var session: MCSession
override init() {
// Ensure MCPeerID is instantiated once
self.myPeerId = MCPeerID(displayName: UIDevice.current.name)
self.session = MCSession(peer: self.myPeerId, securityIdentity: nil, encryptionPreference: .required)
self.serviceAdvertiser = MCNearbyServiceAdvertiser(peer: self.myPeerId, discoveryInfo: nil, serviceType: serviceType)
self.serviceBrowser = MCNearbyServiceBrowser(peer: self.myPeerId, serviceType: serviceType)
super.init()
self.serviceAdvertiser.delegate = self
self.serviceBrowser.delegate = self
self.session.delegate = self
print("Initialized MultipeerManager with peerID: \(self.myPeerId.displayName)")
}
func start() {
print("Starting advertiser and browser...")
serviceAdvertiser.startAdvertisingPeer()
serviceBrowser.startBrowsingForPeers()
}
func stop() {
print("Stopping advertiser and browser...")
serviceAdvertiser.stopAdvertisingPeer()
serviceBrowser.stopBrowsingForPeers()
}
func send(data: Data) {
if !session.connectedPeers.isEmpty {
do {
try session.send(data, toPeers: session.connectedPeers, with: .reliable)
print("Sent data: \(String(data: data, encoding: .utf8) ?? "")")
} catch let error {
print("Error sending data: \(error.localizedDescription)")
}
} else {
print("No connected peers to send data to.")
}
}
}
extension MultipeerManager: MCNearbyServiceAdvertiserDelegate, MCNearbyServiceBrowserDelegate, MCSessionDelegate {
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
print("Received invitation from \(peerID.displayName)")
invitationHandler(true, self.session)
}
func browser(_ browser: MCNearbyServiceBrowser, foundPeer peerID: MCPeerID, withDiscoveryInfo info: [String : String]?) {
print("Found peer: \(peerID.displayName)")
browser.invitePeer(peerID, to: self.session, withContext: nil, timeout: 10)
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {
print("Lost peer: \(peerID.displayName)")
}
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
switch state {
case .connected:
print("Connected to peer: \(peerID.displayName)")
case .connecting:
print("Connecting to peer: \(peerID.displayName)")
case .notConnected:
print("Not connected to peer: \(peerID.displayName)")
@unknown default:
fatalError()
}
}
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
DispatchQueue.main.async {
self.receivedData = String(data: data, encoding: .utf8) ?? ""
print("Received data: \(self.receivedData) from \(peerID.displayName)")
}
}
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {}
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {}
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {}
}
Explanation:
- Singleton
MCPeerID
: TheMCPeerID
is now instantiated once in theMultipeerManager
initializer and reused throughout the class. - Session Delegate: The session delegate is set in the initializer to ensure it handles all session events.
- Logging: Added detailed logging to track the state changes and data transfer between peers, which can help with debugging.
By ensuring MCPeerID
is instantiated only once and reusing the same session, the peer identity remains stable, leading to a more consistent and reliable connection experience.
Related Chats
iOS Local Communication Options 0.479
Thread-Safe Multipeer Manager. 0.474
SwiftUI RealityKit Integration 0.459
Reusable SwiftUI custom view. 0.445
Provide EnvironmentObject TrainerView 0.443
SwiftUI iOS App: Features 0.423
Quit App Button Swift 0.401
Build Mac App in Xcode 0.396