Hello,I am developing an iOS game that will use the Network.framework to communicate between 2-6 iOS devices (hopefully more depending on performance).I began with studying the Apple Tic-Tac-Toe sample project. Like that project, I started with TCP. One device is the server (the source of truth) and the other devices are clients. The server increments a step, and the clients acknowledge the step. I'm trying to achieve a step rate of about 30 - 100ms I want to compare performance with UDP. (I am fairly new to networking, though I use URLSession regularly.)In the WWDC 2019 session Advances in Networking, Part 2 at time 31:30 Tommy Pauly talks about Framing Protocols and shows a slide that reads "Use framing protocols to write common code across TCP and UDP transports."I have not seen an example anywhere of this. Does that mean the GameProtocol class itself can be used with UDP?class GameProtocol: NWProtocolFramerImplementation {
// This class is from the Tic-Tac-Toe project
}I have UDP working in a branch of my code, but it is not using GameProtocol like my TCP branch is.My TCP send:let contentContext = NWConnection.ContentContext(identifier: "GameMessage", metadata: [NWProtocolFramer.Message(gameMessageType: .gameMessage)])
connection.send(content: jsonData, contentContext: contentContext, isComplete: true, completion: .idempotent)My UDP send:connection.send(content: jsonData, completion: .contentProcessed { error in
if let error = error {
DDLogError("**ERROR** UDPConnection send | \(error.localizedDescription)")
}
})So I am interested in knowing more about what was meant by "Use framing protocols to write common code across TCP and UDP transports."Thanks!
Post
Replies
Boosts
Views
Activity
TLDR; Launched my game which uses Network framework – Other Words - https://apps.apple.com/us/app/other-words/id1511220479?mt=8.
I watched Advances in Networking, Part 2 - https://developer.apple.com/videos/play/wwdc2019/713/ and used the WWDC 2019 Tic-Tac-Toe sample app as a starting point. At a past job I had worked with CocoaAsyncSocket for an app that controlled ceiling fans, so I knew some things. I'm not a network expert by any means.
I read through a bunch of threads on these forums. The Network framework documentation could be better. Definitely glad there was some WWDC sample code! Information on creating networked games in general is also hard to come by.
After reading some articles and Stackoverflow answers, I basically broke the game up into lots of micro-steps. Up to 30 steps per second. Steps are sent from server to clients and have to be acknowledged before the game can advance to the next step. This works for this style game on local Wi-Fi, but I have now read that massive multiplayer games have a different approach to account for lag.
My app uses Bonjour and TCP. The player that creates the game is the server (NWListener) and all other players are clients (NWBrowser).
I tried UDP to see if that would help get more messages per second to more clients. (I ended up taking that code out however.) I reduced the bytes of each message by changing property names to just one or two letters. (Not sure if that made much difference.) I shut down the Listener and Browser after the game started because it seemed keeping those running decreased the number of messages (game steps) I could get per second.
A couple things I did differently from the sample code is I have the client side (NWBrowser) instantiate its NWConnection and open it as soon as the browseResultsChangedHandler is called. A player identifier messages is sent when the connection is ready.
The server side (NWListener) gets its Connection instance from newConnectionHandler. When the connection receives the player identifier message from the client, it sends a game invite message.
I may write up a blog post, but if anyone has any questions about my experience creating this app I could answer them here. Thanks!
I created FB13074428 and a small sample project to demonstrate the bug.
https://github.com/jamiemcd/Apple-FB13074428
Basically, if a SwiftData class is marked as @Model and it has a computed property that depends on a relationship's stored property, the computed property does not trigger updates in SwiftUI when the underlying relationship changes its stored property. This is Xcode 15 Beta 8.
@Model
final class Airport {
var code: String
var name: String
var airplanes: [Airplane]
init(code: String, name: String, airPlanes: [Airplane]) {
self.code = code
self.name = name
self.airplanes = airPlanes
}
// Bug: This computed property is not triggering observation in AirportView when airplane.state changes. My understanding of the new observation framework is that it should.
var numberOfAirplanesDeparting: Int {
var numberOfAirplanesDeparting = 0
for airplane in airplanes {
if airplane.state == .departing {
numberOfAirplanesDeparting += 1
}
}
return numberOfAirplanesDeparting
}
}
AirportView should update because it has Text for airport.numberOfAirplanesDeparting
struct AirportView: View {
var airport: Airport
var body: some View {
List {
Section {
ForEach(airport.airplanes) { airplane in
NavigationLink(value: airplane) {
HStack {
Image(systemName: airplane.state.imageSystemName)
VStack(alignment: .leading) {
Text(airplane.name)
Text(airplane.type.name)
}
}
}
}
} header: {
Text(airport.name)
} footer: {
VStack(alignment: .leading) {
Text("Planes departing = \(airport.numberOfAirplanesDeparting)")
Text("Planes in flight = \(airport.numberOfAirplanesInFlight)")
Text("Planes landing = \(airport.numberOfAirplanesLanding)")
}
}
}
}