Sebastian
Sebastian

Reputation: 525

Game Center - sending and receiving data with Swift

I am trying to send the following structure from one player to another:

struct GamePacket {
    var action: String
    var pointArray: [CGPoint]
}

I'm having a hard time figuring out the correct way to convert the GamePacket to Data and back again.

Here is the code I have so far for sending packets:

func send(packet: GamePacket) {
    //convert GamePacket to Data here
    let data = Data.init()

    var remotePlayerArray = [GKPlayer]()

    if let currentPlayer = self.currentPlayer, let match = self.match, let playerArray = self.match?.players {
        for player in playerArray {
            if player != currentPlayer {
                remotePlayerArray.append(player)
            }
        }

        do {
            try match.send(data, to: remotePlayerArray, dataMode: GKMatchSendDataMode.reliable)
        }
        catch {
            print("connectionError")
        }
    }
}

And the code for receiving:

func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {
    //convert Data to GamePacket here
}

From some samples written in ObjectiveC, I managed to convert the GamePacket to Data using something simmilar to the following:

let data = NSData(bytes: &packet, length: MemoryLayout<GamePacket>.size) as Data

However, I can't figure out how to convert the Data back to a GamePacket on the receiving end, nor am I sure this is the correct way to do it.

Any help is greatly apreciated. Thank you.

Upvotes: 4

Views: 955

Answers (2)

Tyler A.
Tyler A.

Reputation: 3068

Use Codable

struct GamePacket: Codable {
  var action: String
  var pointArray: [CGPoint]
}

Then you can convert to Data easily:

func save<T: Encodable>(_ item: T, to url: URL) throws -> Data {
  let encoder = JSONEncoder()
  return try encoder.encode(item)
}
func load<T: Decodable>(from data:Data) throws -> T {
  let decoder = JSONDecoder()
  let item = try decoder.decode(T.self, from: data)
  return item
}

Upvotes: 4

Gero
Gero

Reputation: 4434

A quick and dirty solution would be something like this:

func encodeGamePacket(packet: GamePacket) -> NSData {
    return NSData(bytes: &gamePacket, length: MemoryLayout<GamePacket>.size)
}

func decodeGamePacket(data: NSData) -> GamePacket? {
    var tempBuffer:GamePacket? = nil
    data.getBytes(&tempBuffer, length: MemoryLayout<GamePacket>.size)
    return tempBuffer
}

I have not messed with direct addresses myself under swift yet, so I am not entirely sure whether this is the best approach. Note that I used an optional return type, you can design this differently in your code (maybe add some checks, unwrap the variable and return it or throw an exception when the checks fail).

Alternatively you could design a method that writes your GamePacket into a String (for readability, for example), which you can in turn transform into NSData (String has a data method) or you turn GamePacket into an NSCoding compliant class that offers methods to convert itself into NSData as well.

Upvotes: 2

Related Questions