Posts

Post not yet marked as solved
3 Replies
1.6k Views
I tried to create a framer that splits a stream of ASCII bytes into frames separated by the pipe ascii character: "|". So I made an NWProtocolFramerImplementation as seen in the attached document. Framer Implementation.swift - https://developer.apple.com/forums/content/attachment/fd158172-dcf3-4a00-9e66-fac477ab3c23 The problem is that from the moment I get a chunk that does not end with "|", the framer gets stuck on that chunk. So the other chunks that come after this incomplete chunk never fully arrive in the framer.parseInput(...) call. Because it always parses chunks of minimumIncompleteLength and hence never arrives to the point where the next "|" is. Here is a simple reproduction of this problem: Create a TCP server Setup the server so that it sends chunks of messages when a client connects. Connect to the server (created in 1.) using the framer from above. Start receiving messages. import Network let client = DispatchQueue(label: "Server") let server = DispatchQueue(label: "Client") let networkParameters = NWParameters.tcp networkParameters.defaultProtocolStack.applicationProtocols.insert(NWProtocolFramer.Options(definition: PipeFramer.definition), at: 0) let server = try! NWListener(using: .tcp) server.newConnectionHandler = { connection in &#9;&#9;print("server: new connection from", connection.endpoint) &#9;&#9;print("server (client \(connection.endpoint)): state", connection.state) &#9;&#9;connection.viabilityUpdateHandler = { viable in &#9;&#9;&#9;&#9;print("server (client \(connection.endpoint)): state", connection.state) &#9;&#9;&#9;&#9;if viable { &#9;&#9;&#9;&#9;&#9;&#9;print("server: sending") &#9;&#9;&#9;&#9;&#9;&#9;connection.send(content: "A|Be||Sea".data(using: .utf8)!, isComplete: false, completion: .idempotent) &#9;&#9;&#9;&#9;&#9;&#9;serverQueue.asyncAfter(deadline: .now() + 5) { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;print("server: sending second part") &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;connection.send(content: " is longer than expected|0|".data(using: .utf8)!, isComplete: true, completion: .idempotent) &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;serverQueue.asyncAfter(deadline: .now() + 8) { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;print("server: sending last part") &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;connection.send(content: "Done|".data(using: .utf8)!, isComplete: true, completion: .idempotent) &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;} &#9;&#9;} &#9;&#9;connection.start(queue: serverQueue) } server.stateUpdateHandler = { state in &#9;&#9;print("server:", state) &#9;&#9;if state == .ready, let port = server.port { &#9;&#9;&#9;&#9;print("server: listening on", port) &#9;&#9;} } server.start(queue: serverQueue) let client = NWConnection(to: .hostPort(host: "localhost", port: server.port!), using: networkParameters) func receiveNext() { &#9;&#9;client.receiveMessage { (data, context, complete, error) in &#9;&#9;&#9;&#9;let content: String &#9;&#9;&#9;&#9;if let data = data { &#9;&#9;&#9;&#9;&#9;&#9;content = String(data: data, encoding: .utf8) ?? data.description &#9;&#9;&#9;&#9;} else { &#9;&#9;&#9;&#9;&#9;&#9;content = data?.debugDescription ?? "<no data>" &#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;print("client: received \"\(content)\"", context.debugDescription, complete, error?.localizedDescription ?? "No error") &#9;&#9;&#9;&#9;receiveNext() &#9;&#9;} } client.stateUpdateHandler = { state in &#9;&#9;print("client:", state) &#9;&#9;if state == .ready { &#9;&#9;&#9;&#9;print("client: receiving") &#9;&#9;&#9;&#9;receiveNext() &#9;&#9;} } client.start(queue: clientQueue) Results in: server: waiting(POSIXErrorCode: Network is down) server: ready server: listening on 54894 client: preparing client: ready client: receiving server: new connection from ::1.53179 server (client ::1.53179): state setup server (client ::1.53179): state ready server: sending client: parsing buffer: "A|Be||Sea" client: minLength set to 1 client: parsing buffer: "Be||Sea" client: minLength set to 1 client: parsing buffer: "|Sea" client: minLength set to 1 client: parsing buffer: "Sea" client: minLength set to 4 client: parsing buffer: "" client: minLength set to 1 client: received "A" Optional(Network.NWConnection.ContentContext) true No error client: received "Be" Optional(Network.NWConnection.ContentContext) true No error client: received "<no data>" Optional(Network.NWConnection.ContentContext) true No error client: parsing buffer: "Sea" client: minLength set to 4 server: sending second part client: parsing buffer: "Sea " client: minLength set to 5 client: parsing buffer: "Sea i" client: minLength set to 6 server: sending last part client: parsing buffer: "Sea is" client: minLength set to 7 client: parsing buffer: "Sea is " client: minLength set to 8 Notice that the fourth and fifth message are never received by the client. How should I write the Framer so that it receives messages after an incoming incomplete chunk?
Posted
by damiaan.
Last updated
.
Post not yet marked as solved
1 Replies
1.7k Views
How can we drag views coupled to an NSItemProvider with a custom typeIdentifier or UTI ? I know you can use the following methods in conjunction with predefined UTI types (like kUTTypeText etc.): onDrag(_: () -> NSItemProvider) -> some View - https://developer.apple.com/documentation/swiftui/view/3289004-ondrag onDrop(of: [String], isTargeted: Binding&lt;Bool&gt;?, perform action: ([NSItemProvider]) -> Bool) -> some View - https://developer.apple.com/documentation/swiftui/view/3289007-ondrop But what if I want to use an itemProvider with a custom UTI? I tried: import SwiftUI struct ContentView: View { static let type = "com.devian.sampleapp.customstring.dragdroptype" @State var dropZoneIsHovered = false var body: some View { VStack { Text("Hello") .padding() .onDrag(createItem) Text("Dropzone") .padding() .background(dropZoneIsHovered ? Color.gray : nil) .onDrop( of: [ContentView.type], isTargeted: $dropZoneIsHovered, perform: acceptDrop(items:) ) }.padding()     } func createItem() -> NSItemProvider { NSItemProvider( item: "Hello" as NSString, typeIdentifier: ContentView.type ) } func acceptDrop(items: [NSItemProvider]) -> Bool { print("Hurray we received a custom item") return true } } But the dropzone does not accept custom UTI's I also tried to export the custom UTI using Info.plist: &lt;array&gt; &lt;dict&gt; &lt;key&gt;UTTypeConformsTo&lt;/key&gt; &lt;array&gt; &lt;string&gt;public.data&lt;/string&gt; &lt;/array&gt; &lt;key&gt;UTTypeIdentifier&lt;/key&gt; &lt;string&gt;com.devian.sampleapp.customstring.dragdroptype&lt;/string&gt; &lt;key&gt;UTTypeTagSpecification&lt;/key&gt; &lt;dict/&gt; &lt;/dict&gt; &lt;/array&gt; But that did not work either. Then I tried to add it as a document type in Info.plist &lt;array&gt; &lt;dict&gt; &lt;key&gt;CFBundleTypeRole&lt;/key&gt; &lt;string&gt;None&lt;/string&gt; &lt;key&gt;LSItemContentTypes&lt;/key&gt; &lt;array&gt; &lt;string&gt;com.devian.sampleapp.customstring.dragdroptype&lt;/string&gt; &lt;/array&gt; &lt;key&gt;LSTypeIsPackage&lt;/key&gt; &lt;integer&gt;0&lt;/integer&gt; &lt;/dict&gt; &lt;/array&gt; Nothing of the above works on macOS. What am I doing wrong?
Posted
by damiaan.
Last updated
.
Post not yet marked as solved
2 Replies
930 Views
I need to implement a custom discovery protocol using UDP. It works like this: You broadcast a beacon to a broadcast address (like 255.255.255.255). You wait for clients that respond to your beacon. The problem is: how can I get the addresses of the clients once they send their response? Sending I sent the beacon like this: let connection = NWConnection( to: .hostPort( host: .ipv4(.broadcast), port: .init(integerLiteral: discoveryPort) ), using: .udp ) connection.start(queue: .main) connection.send( content: beacon, completion: .contentProcessed(sendCompleted) ) Receiving To receive the responses from the clients I use connection.receiveMessage { (message: Data?, context: NWConnection.ContentContext?, isComplete: Bool, optionalError: NWError?) in // Great I got a response... // But how do I know where this is coming from? }
Posted
by damiaan.
Last updated
.
Post not yet marked as solved
3 Replies
2.5k Views
Using AppKit you can add NSMenuItems to an NSMenu. I saw that there is something similar to NSMenu in SwiftUI, namely MenuButton. But I cannot find any documentation on how it works. I tried the following:MenuButton("+") { Button("New contact") { print("Create new contact") } Button("New group") { print("Create new group") }}And that looks almost OK butThe buttons have a different background color than the menu (notice the slightly lighter color above and beneath the menu items).When I hover the menu items, their background color doesn't change like a normal macOS menu.I also tried to change the background color manually using the .background() modifier but that doesn't affect the full width of the menu item. I suppose this is because I am placing Buttons inside the MenuButton while it is probably expecting some other SwiftUI element. What elements should I place inside MenuButtons to create a normal-looking macOS menu? See this stackoverflow post for more detailed images that describe what I mean: https://stackoverflow.com/questions/59837991/how-to-add-menu-items-to-a-menubutton-in-swiftui
Posted
by damiaan.
Last updated
.
Post marked as solved
2 Replies
820 Views
I have a SwiftUI view for math expressions. It shows divisions as VStacks with a divider in the middle and additions as HStacks with a "+" sign in the middle. See the images in this GitHub gist: gist.github.com/Dev1an/668978dc4b84b8c6213ba10905d57c1d#proper-alignmentBut I am having trouble aligning the Additions to the Divisions. I can create multiple custom alignment guides beforehand for each division and then everything works fine. But the problem is that I am generating these mathematical expressions dynamically and cannot create new guides (a Type conforming to AlignmentID.Type) for every division in the expression. Is there a way to reuse alignment guides in nested views so that I can reuse the same custom VerticalAlignment multiple times? When I try to reuse the same guide, the HStack's just align to the center (as shown in the gist).Here is an example hierarchy:Addition(alignment: .custom) Division Addition(alignment: .custom) Number + Division Number / (guide: .custom) Number / (guide: .custom) Number + NumberIs there an easy way to solve this? I think it should work a bit similar to VerticalAlign.firstBaseline but it should align to the first division guide down the view hierarchy (when traversing breadth-first) instead. So the Addition on line 01 should use the guide on line 10, and the Addition on line 03 should use the guide on line 08.Any thoughts how to accomplish this?
Posted
by damiaan.
Last updated
.
Post marked as solved
1 Replies
826 Views
When I try to setup a connection, it changes from `waiting(POSIXErrorCode: Network is down)` to preparing state. But it never goes into the ready state.import Network import Dispatch let connection = NWConnection(host: .ipv6(.linkLocalNodes), port: 9898, using: .udp) connection.stateUpdateHandler = { state in print(state) } connection.start(queue: DispatchQueue(label: "UDP"))I found out that this is because there is no interface specified. Because when I setup a connection to .ipv6(IPv6Address("ff02::1%en4")!)it does go into the ready state. But then the interface names don't always stay the same. So how should I figure out all the available interface names? Or is there another way I can send a multicast message on all the supported interfaces?
Posted
by damiaan.
Last updated
.