This code worked for me using dispatch source as pointed out by DTS Engineer. Hopefully it will help out other developers.
final class UDPConnection {
private var socketDescriptor: Int32 = -1
private let port: UInt16
private var dispatchSource: DispatchSourceRead?
init(port: UInt16) {
self.port = port
}
func start() {
socketDescriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
guard socketDescriptor > 0 else {
print("Failed to create socket")
return
}
var broadcastEnable = Int32(1)
setsockopt(socketDescriptor, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, socklen_t(MemoryLayout<Int32>.size))
var address = sockaddr_in()
address.sin_family = sa_family_t(AF_INET)
address.sin_port = in_port_t(port).bigEndian
address.sin_addr = in_addr(s_addr: INADDR_ANY)
let bindResult = withUnsafePointer(to: &address) { pointer in
pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPointer in
bind(socketDescriptor, sockaddrPointer, socklen_t(MemoryLayout<sockaddr_in>.size))
}
}
guard bindResult == 0 else {
print("Failed to bind socket")
close(socketDescriptor)
return
}
print("Started receiving on port \(port)")
// Set up the DispatchSource to monitor the socket for read events
dispatchSource = DispatchSource.makeReadSource(fileDescriptor: socketDescriptor, queue: DispatchQueue.global())
dispatchSource?.setEventHandler { [weak self] in
self?.handleSocketEvent()
}
dispatchSource?.setCancelHandler {
close(self.socketDescriptor)
}
dispatchSource?.resume()
}
private func handleSocketEvent() {
var buffer = [UInt8](repeating: 0, count: 1500)
var address = sockaddr_in()
var addressLength = socklen_t(MemoryLayout<sockaddr_in>.size)
let bytesRead = withUnsafeMutablePointer(to: &address) { pointer in
pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPointer in
recvfrom(self.socketDescriptor, &buffer, buffer.count, 0, sockaddrPointer, &addressLength)
}
}
if bytesRead > 0 {
let message = String(bytes: buffer[..<bytesRead], encoding: .utf8) ?? "Invalid UTF-8 message"
print("Received message: \(message)")
} else if bytesRead == -1 {
print("Error receiving message: \(String(cString: strerror(errno)))")
}
}
}
Post
Replies
Boosts
Views
Activity
Scott, do you mean in the purple Feedback app on the phone? If that is the case, then I am sorry. Can I remove the post?
Thank you so much for actually reading my code and giving recommendations. I am definitely giving the non-blocking I/O solution a shot.
Have a great WWDC this year!
Thanks Quinn for replying. Really appreciate you taking the time to write your response.
I have discovered a lot in the meantime.
I now understand that are different broadcasting modes for UDP: Unicast, Broadcast en Multicast.
TN3151: Choosing the right networking API - Explained:
Network framework supports UDP multicast using the NWConnectionGroup class, but that support has limits and, specifically, it does not support UDP broadcast. If you need something that’s not supported by Network framework, use BSD Sockets.
The game on console was using broadcast mode to broadcast data using UDP. I wrongfully assumed it was the same as multicast.
As has been mentioned in TN3151 Apple does not support UDP broadcast. Hopefully this will change in the future.
Having said that, coming back to your questions:
Are all the peers Apple devices? Or do you have a hardware accessory involved?
A game broadcasts data via UDP. So other than my app that is reading and visualizing that data to the user no Apple devices are involved.
What is role of each peer?
The role of each peer is just to receive the data being broadcasted by the game and processing it in different ways.
Is all traffic multicast? Or do you switch to unicast at some point?
Traffic is broadcast. I made a mistake. The game is able to switch between unicast and broadcast. Unicast works with the Network Framework, broadcast doesn't. Which is a shame, would be incredibly useful if it were.
Is this IPv4? Or IPv6? Or both?
This is IPv4.
What multicast address do you plan to use? And port?
The source (game) will broadcast the data to ALL machines on a local area network on a specific port.
And do you use the same port for both the source and destination?
Correct.
I was able to create code that made it work without the Network Framework.
class UDPBroadcastReceiver {
private var socketDescriptor: Int32 = -1
private let port: UInt16
private let queue = DispatchQueue(label: "UDPBroadcastReceiverQueue")
init(port: UInt16) {
self.port = port
setupSocket()
}
private func setupSocket() {
socketDescriptor = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
guard socketDescriptor > 0 else {
print("Failed to create socket")
return
}
var broadcastEnable: Int32 = 1
let broadcastEnableSize = socklen_t(MemoryLayout.size(ofValue: broadcastEnable))
setsockopt(socketDescriptor, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, broadcastEnableSize)
var address = sockaddr_in(
sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size),
sin_family: sa_family_t(AF_INET),
sin_port: in_port_t(port.bigEndian),
sin_addr: in_addr(s_addr: INADDR_ANY),
sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)
)
let bindResult = withUnsafePointer(to: &address) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
bind(socketDescriptor, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
}
}
guard bindResult == 0 else {
print("Failed to bind socket")
close(socketDescriptor)
return
}
print("Socket setup and bound to port \(port)")
receive()
}
private func receive() {
queue.async {
var buffer = [UInt8](repeating: 0, count: 1024)
while true {
let bytesRead = recv(self.socketDescriptor, &buffer, buffer.count, 0)
if bytesRead > 0 {
let data = Data(buffer[0..<bytesRead])
// To Do: Parse data
} else if bytesRead == 0 {
print("Socket closed by peer")
break
} else {
print("Error receiving data: \(String(cString: strerror(errno)))")
break
}
}
}
}
deinit {
if socketDescriptor > 0 {
close(socketDescriptor)
}
}
}
I also don't understand why an entitlement is needed, why wouldn't a privacy notification suffice?
Xcode 15 Beta 8 the issue has not been resolved.
Updated iPhone en Watch to the latest developer version
Unpaired the Watch and then pair it again
Turned Wifi off on the Mac, Turned it back on
Opened the Watch app on the iPhone
Turned airplane mode on all and back on
Deleted everything in the cache (~/Library/Developer/Xcode)
Nothing works.
Seeing the same issue with Xcode 15 Beta 7 even though it says watchOS connectivity has been resolved.
It says Waiting to reconnect to Apple Watch van Mark, Xcode will continue when the operation completes. (See image)
I tried:
Completly erasing the Apple Watch
Update iPhone to the latest Public Beta version
Update Apple Watch to latest Public Beta version
Repair the Apple Watch with the iPhone
Connecting the iPhone with a cable
Nothing helped.
Appearantly no Apple engineer or community member knew the answer. I finally found a work-around and I am happy to share it with the people also struggling with the same issue which still exists in the latest Xcode Beta.
import SwiftUI
@main
struct YourApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
MainView()
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidBecomeActive(_ notification: Notification) {
if let window = NSApp.windows.first {
window.deminiaturize(nil)
}
}
func applicationDidChangeOcclusionState(_ notification: Notification) {
if let window = NSApp.windows.first, window.isMiniaturized {
NSWorkspace.shared.runningApplications.first(where: {
$0.activationPolicy == .regular
})?.activate(options: .activateAllWindows)
}
}
}
We are now in the year 2021 where Apple just introduced iOS 15. Looking at the CallKit documentation there is has not been an update. I really don't understand why you have to enter a list of numbers while patterns can be so much more efficient. Unwanted phone calls are a growing pain.
Please Apple LISTEN to the feedback of your customers!
If you want to block certain country codes you need to generate and add millions of phone numbers in the way the current API is build up. It's awkward.
Bump (+1) I like to know this as well.
When you go to Settings > Game Center. You will notice you can easily choose and create an avatar by tapping the profile picture. I like my app to do the same. I want to call a sheet in SwiftUI that will return an Avatar Image so I can use that as the profile picture.
I fourth that.
I love that Apple exposed Group Activity as an API. Would only make sense to make DrawTogether available as sample code.
I don't really understand your question, but I wrote an article about everything you should about @AppStorage and @SceneStorage on Medium, which might answer your question better.
Perhaps you don't realize that with defining @AppStorage you defined a source-of-truth. If you need to access it "outside" the views perhaps its better to place the @AppStorage somewhere higher in the view hierarchy and pass it with bindings.
Links: Introducing @AppStorage in SwiftUI (medium.com/swlh/introducing-appstorage-in-swiftui-470a56f5ba9e)
Introducing @SceneStorage in SwiftUI: (medium.com/@crystalminds/introducing-scenestorage-in-swiftui-5a4ec1a90ca3)
I also submitted feedback. Apple we need this .listSeparatorStyle(.none).
Radar #FB8654253
UISearchController works when attached to a List. It becomes buggy when you use something other like a ScrollView or VStack. I have reported this in Apple's Feedback app and I hope others will do the same.
Never the less if you like to include a UISearchController in your App.
I created a Swift Package at Github named NavigationSearchBar. - https://github.com/markvanwijnen/NavigationSearchBar
Apple Feedback
The client code is recreating the Model every time the view is rendered. The model generates the new ID for every item every time. Without consistent IDs, the system cannot maintain a consistent UI.
The client code should be using @StateObject on the model property in ContentView.