I'm working on a Multiplayer Swift (XCode 7.3 / iOS 9.2) game using GameKit.
I'm using a custom struct as the information sent via GameKit. Copy of the struct is below. frustrationNetworkMessage is a String enum & Card is a seperate class (can post code if needed)
struct frustrationNetworkMessage {
var messageType : FrustrationNetworkMessageType
var cards : [Card]
var actingPlayer : String
init(messageType : FrustrationNetworkMessageType, cards : [Card], actingPlayer : String) {
self.messageType = messageType
self.cards = cards
self.actingPlayer = actingPlayer
}
My issue is in the 'match(match: GKMatch, didReceiveData data: NSData, fromRemotePlayer player: GKPlayer)' method. I've worked out this is being called once some data is received:
private var receivedData : NSMutableData
func decode<T>(data: NSData) -> T {
let pointer = UnsafeMutablePointer<T>.alloc(sizeof(T.Type))
data.getBytes(pointer, length: sizeof(T))
return pointer.move()
}
func match(match: GKMatch, didReceiveData data: NSData, fromRemotePlayer player: GKPlayer) {
print("Match / did receive data: size of data - \(sizeofValue(data))")
receivedData.appendData(data)
if let receivedNetworkMessage : frustrationNetworkMessage = decode(receivedData) {
print("Match / did receive data: \(receivedNetworkMessage.messageDetails())")
When I run the above code it crashes at the final print statement with EXC_BAD_ACCESS (which I understand is it complaining it's trying to access part of the struct that has not been received).
As an example:
Message summary at sent -> sendNetworkMessage: Message type: startNewGame with actingPlayer: G:118279601 & card count: 1 - message size: 40
Message length first time match didReceiveData is called -> Match / did receive data: size of data - 8
How can I check I've gotten the entire struct before acting on it? I had thought of updating the struct to include a length field (in theory I should always receive that in one go - so could use it to check the packet length).
You can use a background process like this to check your data structure continuously?
newQueue = NSOperationQueue()
let operation2 = NSBlockOperation(block: {
})
operation2.completionBlock = {
print("Operation 2 completed")
}
let operation1 = NSBlockOperation(block: {
})
operation1.completionBlock = {
self.newQueue.addOperation(operation2)
}
operation1.qualityOfService = .UserInitiated
newQueue.addOperation(operation1)
}
Initialise the variables and then check their length; this will save/stop it crashing.
I've found an answer for the minute (issue has now moved elsewhere in the code)
struct frustrationNetworkMessage {
var messageType : FrustrationNetworkMessageType
var card : Card
var actingPlayer : String
init(messageType : FrustrationNetworkMessageType, card : Card, actingPlayer : String) {
self.messageType = messageType
self.card = card
self.actingPlayer = actingPlayer
}
init(data : NSData) {
let messageTypeLength = sizeofValue(FrustrationNetworkMessageType)
let cardLength = sizeofValue(Card)
let messageTypeRange = NSMakeRange(0, messageTypeLength)
let cardLengthRange = NSMakeRange(messageTypeLength, messageTypeLength + cardLength)
let actingPlayerRange = NSMakeRange(messageTypeLength + cardLength, data.length)
self.messageType = .didDiscardCard
self.card = Card.emptyCard()
data.getBytes(&self.messageType, range: messageTypeRange)
data.getBytes(&self.card, range: cardLengthRange)
let actingPlayerData = data.subdataWithRange(actingPlayerRange)
self.actingPlayer = NSString(data: actingPlayerData, encoding: NSUTF8StringEncoding) as! String
}
func archive()->NSData {
var localMessageType = messageType.rawValue
var localCard = card
let returnValue = NSMutableData(bytes: &localMessageType, length: sizeofValue(self.messageType.rawValue))
returnValue.appendData(NSData(bytes: &localCard, length: sizeofValue(self.card)))
returnValue.appendData(actingPlayer.dataUsingEncoding(NSUTF8StringEncoding)!)
return returnValue
}
What I've found through trial an error (and a lot of follow-up reading):
match didReceiveData only seems to get called when it has received an entire packet (so don't need to handle receiving partial data)
Where possible avoid encoding class data types as NSData (through digging I found I was only encoding a local address, works fine if passing locally - not so much over the network). Close to all of my data types as either basic ones (all enums are now of type UInt8) or structs.
Related
I am reading the German e-Passport and it's reading the Datagroups 1,2,3 & 14 and SOD, COM also. Now, I want to read the Datagroup 11 which hold the additional details. But the German Passport doesn't read the optional data. So, how do I know Which passports read which Groups?
I have gone through the ICAO 9303 but didn't get any chance to get this information.
In my app I read DG32, DG33, DG34, but flow is the same, here is some example of how to implement DG11 file reading using NFCPassportReader
public class DataGroup {
public var elements: [String: String] = [:]
// I added this dictionary to get value by key from documentation
class DG11: DataGroup {
private let tags = [0x5F0E, 0x5F0F, 0x5F10, 0x5F11, 0x5F2B]
required init(_ data: [UInt8]) throws {
try super.init(data)
datagroupType = .DG11
}
override func parse(_ data: [UInt8]) throws {
var tag = try getNextTag()
if tag != 0x5C { throw TagError.InvalidResponse }
_ = try getNextValue()
try tags.forEach { _ in
tag = try getNextTag()
parseBody(try getNextValue(), key: String(tag, radix: 16, uppercase: true))
}
print(elements)
}
private func parseBody(_ data: [UInt8], key: String) {
elements[key] = String(bytes: data[0...], encoding: .utf8)
}
}
Hope it helps
I have a BLE system where the peripheral sends parts of a whole message and I need to wait for the whole message before processing it. Within peripheral(didUpdateValueFor characteristic:) I process the messages and whenever I get a complete one, I set flagDataAvailable = true. And here's where I'm having problems:
// This function is called from another class
override func get(_ data: [UInt8]) -> [UInt8] {
var payloadGet = [UInt8(bitPattern: Int8(-1))]
payloadGet = getOrPut(data, false)
return payloadGet
}
func getOrPut(_ data: [UInt8], _ isPut: Bool) -> [UInt8] {
var command = [UInt8]()
var payload = [UInt8]()
if isPut {
command = CommunicationDevice.buildPutDataApdu(data)
} else {
command = CommunicationDevice.buildGetDataApdu(data)
}
print("Sending command \(command)")
bluetoothDevice.writeValue(Data(command), for: notifyCharacteristic, type: CBCharacteristicWriteType.withResponse)
let resultLength = read()
let statusWord: [UInt8] = [rxBuffer[resultLength - 2], rxBuffer[resultLength - 1]]
if (CommunicationDevice.TRANSMISSION_OK == statusWord) {
payload = Array(rxBuffer[0..<resultLength - 2])
}
return payload
}
func read() -> Int {
// Here I need to wait until flagDataAvailable is true before continuing
var nbBytes = 0
flagDataAvailable = false
nbBytes = rxBufferLength
rxBufferLength = 0
return nbBytes
}
I've tried using uwait(delay) but then I never receive the callbacks for the characteristic update.
Clarifying:
The change in flagDataAvailable is being made in the same class as the function that need to track it. My problem is in how to wait for that change without blocking the thread nor returning the function read() before there is data available
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.
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
}
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.
In my app I am doing something like this:
struct Record {
var exampleData : String
}
class ExampleClass : UIViewController {
let records = [Record]()
override func viewDidLoad() {
super.viewDidLoad()
let data = NSKeyedArchiver.archivedDataWithRootObject(self.records) as! NSData
}
...
}
But in the last line of viewDidLoad() I got this error:
Argument type '[Record]' does not conform to expected type 'AnyObject'
How can I fix this? Thanks.
If you want to keep struct, you can encode data using withUnsafePointer(). Here's an example, which I adapted from this Gist:
import UIKit
enum EncodingStructError: ErrorType {
case InvalidSize
}
func encode<T>(var value: T) -> NSData {
return withUnsafePointer(&value) { p in
NSData(bytes: p, length: sizeofValue(value))
}
}
func decode<T>(data: NSData) throws -> T {
guard data.length == sizeof(T) else {
throw EncodingStructError.InvalidSize
}
let pointer = UnsafeMutablePointer<T>.alloc(1)
data.getBytes(pointer, length: data.length)
return pointer.move()
}
enum Result<T> {
case Success(T)
case Failure
}
I added some error handling and marked the method as throws. Here's one way you can use it, in a do…catch block:
var res: Result<String> = .Success("yeah")
var data = encode(res)
do {
var decoded: Result<String> = try decode(data)
switch decoded {
case .Failure:
"failure"
case .Success(let v):
"success: \(v)" // => "success: yeah"
}
} catch {
print(error)
}
The error handling I added will not decode if the NSData length doesn't match the type size. This can commonly happen if you write the data to disk, the user updates to a newer version of the app with a different-sized version of the same type, and then the data is read in.
Also note that sizeof() and sizeofValue() may return different values on different devices, so this isn't a great solution for sending data between devices (NSJSONSerialization might be better for that).
AnyObject means any reference type object, primarily a class. A struct is a value type and cannot be passed to a function needing an AnyObject. Any can be used to accept value types as well as reference types. To fix your code above, change struct Record to class Record. But I have a feeling you may want to use a struct for other reasons. You can create a class wrapper around Record that you can convert to and from to use for functions that need an AnyObject.
I did a similar thing:
static func encode<T>(value: T) -> NSData {
var val = value
return withUnsafePointer(to: &val) { pointer in
NSData(bytes: pointer, length: MemoryLayout.size(ofValue: val))
}
}
static func decode<T>(data: NSData) -> T {
guard data.length == MemoryLayout<T>.size.self else {
fatalError("[Credential] fatal unarchiving error.")
}
let pointer = UnsafeMutablePointer<T>.allocate(capacity: 1)
data.getBytes(pointer, length: data.length)
return pointer.move()
}
This question already has answers here:
How can I get a real IP address from DNS query in Swift?
(3 answers)
Closed 2 years ago.
I'm trying to do a simple DNS lookup in swift. So far, here is the code that I have:
let hostRef = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue()
var resolved = CFHostStartInfoResolution(hostRef, CFHostInfoType.Addresses, nil)
let addresses = CFHostGetAddressing(hostRef, &resolved).takeRetainedValue() as NSArray
At this point, each element in the "addresses" NSArray is a CFDataRef object wrapping a sockaddr struct.
Since CFDataRef can be toll-free bridged to NSData, I can loop through them like so:
for address: AnyObject in addresses {
println(address) // address is of type NSData.
}
So far so good (I think). This prints out valid looking data when I run it in a unit test. Here is where I get stuck though. For the life of me, I can't figure out how to convert the bytes in the NSData object into a sockaddr struct.
How can I convert address.bytes, which is of type COpaquePointer?, into a c struct? Any help appreciated. I'm banging my head against the wall trying to figure this out.
For a simpler solution using getnameinfo, see Martin's answer here: How can I get a real IP address from DNS query in Swift?
Updated for Swift 5 / IPv6:
The objects returned by CFHostGetAddressing can be bridged to Swift as Data, and cast to in_addr/in6_addr by using withUnsafeBytes and assumingMemoryBound(to:).
Here's a complete example that uses inet_ntop to convert IPv4/IPv6 addresses to strings:
import CFNetwork
import Foundation
protocol NetworkAddress {
static var family: Int32 { get }
static var maxStringLength: Int32 { get }
}
extension in_addr: NetworkAddress {
static let family = AF_INET
static let maxStringLength = INET_ADDRSTRLEN
}
extension in6_addr: NetworkAddress {
static let family = AF_INET6
static let maxStringLength = INET6_ADDRSTRLEN
}
extension String {
init<A: NetworkAddress>(address: A) {
// allocate a temporary buffer large enough to hold the string
var buf = ContiguousArray<Int8>(repeating: 0, count: Int(A.maxStringLength))
self = withUnsafePointer(to: address) { rawAddr in
buf.withUnsafeMutableBufferPointer {
String(cString: inet_ntop(A.family, rawAddr, $0.baseAddress, UInt32($0.count)))
}
}
}
}
func addressToString(data: Data) -> String? {
return data.withUnsafeBytes {
let family = $0.baseAddress!.assumingMemoryBound(to: sockaddr_storage.self).pointee.ss_family
// family determines which address type to cast to (IPv4 vs IPv6)
if family == numericCast(AF_INET) {
return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in.self).pointee.sin_addr)
} else if family == numericCast(AF_INET6) {
return String(address: $0.baseAddress!.assumingMemoryBound(to: sockaddr_in6.self).pointee.sin6_addr)
}
return nil
}
}
let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue()
var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil))
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [Data]?
print(addresses?.compactMap(addressToString))
You can use the NSData method getBytes(_, length:) method and pass the sockaddr struct to the inout parameter using the prefix & operator:
var data: NSData ...
var address: sockaddr ...
data.getBytes(&address, length: MemoryLayout<sockaddr>.size)
Updated for Swift 3:
let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com" as CFString).takeRetainedValue()
var resolved = DarwinBoolean(CFHostStartInfoResolution(host, .addresses, nil))
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?
if let data = addresses?.first {
var storage = sockaddr_storage()
data.getBytes(&storage, length: MemoryLayout<sockaddr_storage>.size)
if Int32(storage.ss_family) == AF_INET {
let addr4 = withUnsafePointer(to: &storage) {
$0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee
}
}
// prints 74.125.239.132
print(String(cString: inet_ntoa(addr4.sin_addr), encoding: .ascii))
}
}
Updated 6/3/2015:
Now that C structs can be easily zero-initialized, this becomes much simpler:
let host = CFHostCreateWithName(kCFAllocatorDefault, "google.com").takeRetainedValue()
var resolved = CFHostStartInfoResolution(host, .Addresses, nil)
let addresses = CFHostGetAddressing(host, &resolved)?.takeUnretainedValue() as! [NSData]?
if let data = addresses?.first {
var storage = sockaddr_storage()
data.getBytes(&storage, length: sizeof(sockaddr_storage))
if Int32(storage.ss_family) == AF_INET {
let addr4 = withUnsafePointer(&storage) { UnsafePointer<sockaddr_in>($0).memory }
// prints 74.125.239.132
println(String(CString: inet_ntoa(addr4.sin_addr), encoding: NSASCIIStringEncoding))
}
}
Unfortunately this requires sockaddr to be initialized first. To avoid that, you could do something like this:
func makeWithUnsafePointer<T>(body: UnsafePointer<T> -> ()) -> T {
let ptr = UnsafePointer<T>.alloc(sizeof(T))
body(ptr)
return ptr.move()
}
let addr: sockaddr = makeWithUnsafePointer {
data.getBytes($0 as UnsafePointer<sockaddr>, length: sizeof(sockaddr))
}
Or this:
func makeWithUninitialized<T>(body: inout T -> ()) -> T {
let ptr = UnsafePointer<T>.alloc(sizeof(T))
body(&ptr.memory)
return ptr.move()
}
let addr = makeWithUninitialized { (inout addr: sockaddr) in
data.getBytes(&addr, length: sizeof(sockaddr))
}
For more discussion, see Swift: Pass Uninitialized C Structure to Imported C function