There seems to be a misunderstanding: for each type T all instances of T have the same size that can be calculated as sizeof(T) . In the case of arrays, there may be indents between the elements of the array, so the total size required for arr1 is
arr1.count * strideof(Int)
(Compare, for example, Swift: how to use sizeof? For subtle differences between sizeof() and strideof() ).
Therefore, the general function for creating NSData from an array will be
extension Array { func asData() -> NSData { return self.withUnsafeBufferPointer({ NSData(bytes: $0.baseAddress, length: count * strideof(Element)) }) } }
Using withUnsafeBufferPointer() ensures that the array uses continuous storage for its elements.
In the case of "simple" types such as Int and Float , this gives the expected results:
let arr1 = [1, 2, 3] print(arr1.asData()) // <01000000 00000000 02000000 00000000 03000000 00000000> let arr2 = [1.0, 23556789000.0] print(arr2.asData()) // <00000000 0000f03f 0000204c 60f01542>
However, this is useless for an array of strings:
let arr3 = ["hello", "ok", "👍"] print(arr3.asData()) // <945b2900 01000000 05000000 00000000 00000000 00000000 9a5b2900 01000000 02000000 00000000 00000000 00000000 068d2900 01000000 02000000 00000080 00000000 00000000>
since struct String contains (hidden / undocumented) pointers to the actual storage of characters.
One possibility is to add each line as a NUL-terminated UTF-8 String:
let data3 = NSMutableData() arr3.forEach { string in string.withCString { data3.appendBytes($0, length: Int(strlen($0)) + 1) } } print(data3) // <68656c6c 6f006f6b 00f09f91 8d00>
Alternatively, use NSKeyedArchiver , as in the streams you referenced.