Search code examples
swiftstructbytearraysnsdata

Using Structs (includes byte Array) with SWIFT - Struct to NSData and NSData to Struct


In continuation of my original post: Using Structs (Bytes) with SWIFT - Struct to NSData and NSData to Struct

I'm now finding that if I have a struct with bytes mixes with a byte array it doesn't pack correctly.

Example Code:

struct exampleStruct {
    var ModelNumber: Byte
    var MajorVersion: Byte
    var MinorVersion: Byte
    var Revision: Byte
    var Message: [Byte]
}

var myStruct = exampleStruct (
    ModelNumber: 1,
    MajorVersion: 2,
    MinorVersion: 3,
    Revision: 4,
    Message: [0x48, 0x45, 0x4C, 0x4C, 0x4F] // HELLO
)

println(myStruct.Message) returns correct array with values of [72,69,76,76,79]

However, when I convert this struct to NSData using:

// Struct to NSData.
var data = NSData(
    bytes: & myStruct,
    length: sizeof(exampleStruct)
)

I get unexpected results of: "data: <01020304 00000000 108c91fd a87f0000>". I was expecting "data: <01020304 48454c4c 4f>"

It seems like this is because the [Byte] Array length is not set. Can it be set in SWIFT? When I try the following:

struct exampleStruct {
   var ModelNumber: Byte
   var MajorVersion: Byte
   var MinorVersion: Byte
   var Revision: Byte
   var Message: Byte[5] // << Set array length 5
}

I get a warning that states: "Fixed-length arrays are not yet supported".

Anyway to work around this limitation?


Solution

  • var Message: [Byte] declares a variable of the type struct Array:

    struct Array<T> : MutableCollectionType, Sliceable {
    
        /// The type of element stored by this `Array`
        typealias Element = T
    
        /// Always zero, which is the index of the first element when non-empty.
        var startIndex: Int { get }
    
        /// A "past-the-end" element index; the successor of the last valid
        /// subscript argument.
        var endIndex: Int { get }
        subscript (index: Int) -> T
    
        // ... and much more ...
    }
    

    so this is not just a "C array" of bytes. The actual storage is opaque and only accessible through methods and properties.

    You can define a tuple of fixed size:

    struct exampleStruct {
        var ModelNumber: Byte
        var MajorVersion: Byte
        var MinorVersion: Byte
        var Revision: Byte
        var Message: (Byte, Byte, Byte, Byte, Byte)
    }
    
    var myStruct = exampleStruct (
        ModelNumber: 1,
        MajorVersion: 2,
        MinorVersion: 3,
        Revision: 4,
        Message: (0x48, 0x45, 0x4C, 0x4C, 0x4F) // HELLO
    )
    var data = NSData(
        bytes: &myStruct,
        length: sizeof(exampleStruct)
    )
    
    println(data) // <01020304 48454c4c 4f>
    

    However, I don't think that Swift makes any guarantees about the binary representation of its structures, so this may break in the future.