Storing Swift value types in Core Data - swift

Is there a way to store Swift's value types, such as enums or structs in Core Data?
What's the best way of doing this?

It's actually pretty easy: Just initialise NSData with the bytes of your value type.
Your value type:
struct MyStruct {
let x : Int
let y : Bool
let z : Double
}
Functions to set and get the data
func valueToData<T>(var t: T) -> NSData {
return NSData(bytes: &t, length: sizeof(T))
}
func dataToValue<T>(inout t: T, data: NSData) {
memcpy(&t, data.bytes, data.length)
}
Tests:
let originalValue = MyStruct(x: 3, y: true, z: 1.3)
let data = valueToData(originalValue)
// Save data to CoreData
var s = MyStruct(x: 0, y: false, z: 0) // Empty Value
dataToValue(&s, data: data) // Initialize with data
// s is MyStruct(x: 3, y: true, z: 1.3)

A suitable way for enum is to declare the enum with a raw type supported by Core Data and use a custom instance property to convert the enum case to raw value and vice versa.
For example:
enum State : Int32 {
case None, Empty, Full
}
#NSManaged var state: Int32
var stateEnum: State {
get {
return State(rawValue: state)!
}
set {
state = newValue.rawValue
}
}
A struct has to be "serialized" in a similar way – maybe it could be even represented by an Core Data entity – but this depends on the struct(ure) of the struct.

Related

How to store a generic Measurement in Swift?

I'd like to have a variable which can represent a single Measurement but may have different unit types. For example, it could store a length or a mass. It seems so simple, but I can't figure it out.
Here's what I tried:
struct Data {
var weight: Measurement<UnitMass>
var length: Measurement<UnitLength>
var target = "Weight"
var valueOfTarget: Measurement<Unit> {
if target == "Weight" {
return weight
} else {
return length
}
}
}
var data = Data(weight: Measurement<UnitMass>(value: 10, unit: UnitMass.kilograms),
length: Measurement<UnitLength>(value: 10, unit: UnitLength.centimeters),
target: "Weight")
print(data.valueOfTarget)
I also tried using <Dimension> as suggested in another answer, but that had a similar error.
This results in a compiler error:
error: cannot convert return expression of type 'Measurement<UnitMass>' to return type 'Measurement<Unit>'
Am I'm missing something obvious or is this just not possible?
You could just create new generic return values. This seems to compile OK for me.
struct Data {
var weight: Measurement<UnitMass>
var length: Measurement<UnitLength>
var target = "Weight"
var valueOfTarget: Measurement<Unit> {
if target == "Weight" {
return Measurement<Unit>(value: weight.value, unit: weight.unit)
} else {
return Measurement<Unit>(value: length.value, unit: length.unit)
}
}
}
var data = Data(weight: Measurement<UnitMass>(value: 10, unit: UnitMass.kilograms),
length: Measurement<UnitLength>(value: 10, unit: UnitLength.centimeters),
target: "Weight")
print(data.valueOfTarget)
First, don't make your own struct named Data, because Foundation already has a type named Data that's pretty widely used.
Second, since (in a comment) you said “It's for weight loss, the user can choose a target as either a weight or a waist size”, it seems like you should probably model this using an enum rather than a struct:
enum WeightLossTarget {
case weight(Measurement<UnitMass>)
case waistSize(Measurement<UnitLength>)
}
Third, if you really need to use a struct, you can fall back to the Objective-C type NSMeasurement for non-generic type:
struct WeightLossTarget {
enum TargetType {
case weight
case waistSize
}
var weight: Measurement<UnitMass>
var waistSize: Measurement<UnitLength>
var target: TargetType
var valueOfTarget: NSMeasurement {
switch target {
case .weight: return weight as NSMeasurement
case .waistSize: return waistSize as NSMeasurement
}
}
}

What is the best way in Swift 4+ to store a set of homogenous arrays for various types in a dictionary?

Consider a situation where we want to have a dictionary of arrays, with each array being a homogeneous collection of values of some type (which may be a struct or a primitive type). I'm currently using the ObjectIdentifier of the type defining it thusly:
let pInts : [UInt32] = [4, 6, 99, 1001, 2032]
let pFloats : [Float] = [3.14159, 8.9]
let pBools : [Bool] = [true, false, true]
let myDataStructure : [ObjectIdentifier : [Any]] = [
ObjectIdentifier(Float.self) : pFloats,
ObjectIdentifier(UInt32.self) : pInts,
ObjectIdentifier(Bool.self) : pBools
]
The issue here is that when traversing the data structure, Swift doesn't know that the objects in each list are homogeneous. Since swift is statically typed, I'm guessing it is not possible to typecast the [Any] lists using the ObjectIdentifier keys. Consider this traversal pseudocode:
for (typeObjId, listOfValuesOfSometype) in myDataStructure {
// do something like swap values around in the array,
// knowing they are homogeneously but anonymously typed
}
So, is there some metatype machinery I can concoct to represent this data structure in a way that does not anticipate the list of actual types that will have arrays in it?
I'm not exactly sure what you want to accomplish, Inside the dictionary loop the arrays will always be of type Any, but if you want to move items in the arrays around, you could just do that. Just reassign the array first to a var and then put it back in the dictionary.
If you do want to loop through the items of a specific type, then you could use the array helper function below.
func testX() {
let pInts: [UInt32] = [4, 6, 99, 1001, 2032]
let pFloats: [Float] = [3.14159, 8.9]
let pBools: [Bool] = [true, false, true]
var myDataStructure: [ObjectIdentifier: [Any]] = [
ObjectIdentifier(Float.self): pFloats,
ObjectIdentifier(UInt32.self): pInts,
ObjectIdentifier(Bool.self): pBools
]
// Swap the first 2 items of every array
for d in myDataStructure {
var i = d.value
if i.count > 1 {
let s = i[0]
i[0] = i[1]
i[1] = s
}
myDataStructure[d.key] = i
}
// Now dump all data per specific type using the array helper function.
for i: UInt32 in array(myDataStructure) {
print(i)
}
for i: Float in array(myDataStructure) {
print(i)
}
for i: Bool in array(myDataStructure) {
print(i)
}
}
func array<T>(_ data: [ObjectIdentifier: [Any]]) -> [T] {
return data[ObjectIdentifier(T.self)] as? [T] ?? []
}

How to convert UInt16 to UInt8 in Swift 3?

I want to convert UInt16 to UInt8 array, but am getting the following error message:
'init' is unavailable: use 'withMemoryRebound(to:capacity:_)' to
temporarily view memory as another layout-compatible type.
The code:
let statusByte: UInt8 = UInt8(status)
let lenghtByte: UInt16 = UInt16(passwordBytes.count)
var bigEndian = lenghtByte.bigEndian
let bytePtr = withUnsafePointer(to: &bigEndian) {
UnsafeBufferPointer<UInt8>(start: UnsafePointer($0), count: MemoryLayout.size(ofValue: bigEndian))
}
As the error message indicates, you have to use withMemoryRebound()
to reinterpret the pointer to UInt16 as a pointer to UInt8:
let bytes = withUnsafePointer(to: &bigEndian) {
$0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: bigEndian)) {
Array(UnsafeBufferPointer(start: $0, count: MemoryLayout.size(ofValue: bigEndian)))
}
}
The closures are invoked with pointers ($0) which are only valid
for the lifetime of the closure and must not be passed to the outside
for later use. That's why an Array is created and used as return value.
There is a simpler solution however:
let bytes = withUnsafeBytes(of: &bigEndian) { Array($0) }
Explanation: withUnsafeBytes invokes the closure with a UnsafeRawBufferPointer to the storage of the bigEndian variable.
Since UnsafeRawBufferPointer is a Sequence of UInt8, an array
can be created from that with Array($0).
You can extend Numeric protocol and create a data property as follow:
Swift 4 or later
extension Numeric {
var data: Data {
var source = self
return Data(bytes: &source, count: MemoryLayout<Self>.size)
}
}
Since Swift 3 Data conforms to RandomAccessCollection so you can just create an array of bytes from your UInt16 bigEndian data:
extension Data {
var array: [UInt8] { return Array(self) }
}
let lenghtByte = UInt16(8)
let bytePtr = lenghtByte.bigEndian.data.array // [0, 8]

How to change the value of a child from a Mirror introspection

I'm doing a bunch of BLE in iOS, which means lots of tight packed C structures being encoded/decoded as byte packets. The following playground snippets illustrate what I'm trying to do generically.
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
So given a series of fields of varying size, we don't get the "tightest" packing of bytes. Pretty well known and accepted. Given my simple structs, I'd like to be able to arbitrarily encode the fields back to back with no padding or alignment stuff. Relatively easy actually:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
Huzzah! Works as expected. Could probably add a Class around it, or maybe a Protocol extension and have a nice utility. The problem I'm up against is the reverse process:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
Given an unpopulated struct value, I can decode the byte stream appropriately, figure out what the new value would be for each field. What I don't know how to do is to actually update the target struct with the new value. In my example above, I show how I might do it with C, get the pointer to the original child, and then update its value with the new value. I could do it easily in Python/Smalltalk/Ruby. But I don't know how one can do that in Swift.
UPDATE
As suggested in comments, I could do something like the following:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
Basically, I move the various stream decoding methods to byte stream (i.e. GeneratorType where Element == UInt8), and then I just have to write an initializer that strings those off in the same order and type the struct is defined as. I guess that part, which is essentially "copying" the structure definition itself (and therefore error prone), is what I had hoped to use some sort of introspection to handle. Mirrors are the only real Swift introspection I'm aware of, and it seems pretty limited.
As discussed in the comments, I suspect this is over-clever. Swift includes a lot of types not friendly to this approach. I would focus instead on how to make the boilerplate as easy as possible, without worrying about eliminating it. For example, this is very sloppy, but is in the direction I would probably go:
Start with some helper packer/unpacker functions:
func pack(values: Any...) -> [UInt8]{
var output:[UInt8] = []
for value in values {
switch value {
case let i as UInt32:
(0...3).forEach { output.append(UInt8((i >> ($0 * 8)) & 0xFF)) }
case let i as UInt8:
output.append(i)
default:
assertionFailure("Don't know how to serialize \(value.dynamicType)")
}
}
return output
}
func unpack<T>(bytes: AnyGenerator<UInt8>, inout target: T) throws {
switch target {
case is UInt32:
var newValue: UInt32 = 0
(0...3).forEach {
newValue |= UInt32(bytes.next()!) << UInt32($0 * 8)
}
target = newValue as! T
case is UInt8:
target = bytes.next()! as! T
default:
// Should throw an error here probably
assertionFailure("Don't know how to deserialize \(target.dynamicType)")
}
}
Then just call them:
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
func encode() -> [UInt8] {
return pack(a, b, c)
}
static func decode(bytes: [UInt8]) throws -> Thing {
var thing = Thing()
let g = anyGenerator(bytes.generate())
try unpack(g, target: &thing.a)
try unpack(g, target: &thing.b)
try unpack(g, target: &thing.c)
return thing
}
}
A little more thought might be able to make the decode method a little less repetitive, but this is still probably the way I would go, explicitly listing the fields you want to encode rather than trying to introspect them. As you note, Swift introspection is very limited, and it may be that way for a long time. It's mostly used for debugging and logging, not logic.
I have tagged Rob's answer is the official answer. But I'd thought I'd share what I ended up doing as well, inspired by the comments and answers.
First, I fleshed out my "Problem" a little to include a nested structure:
struct Inner {
var ai:UInt16 = 0
var bi:UInt8 = 0
}
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var inner = Inner()
var c:UInt8 = 0
}
sizeof(Thing) // --> 12 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, inner: Inner(ai: 0x1122, bi: 0xDD), c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 2211dd13> :(
For Arbitrary Packing, I stuck with the same generic approach:
protocol Packable {
func packed() -> [UInt8]
}
extension UInt8:Packable {
func packed() -> [UInt8] {
return [self]
}
}
extension UInt16:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF))]
}
}
extension UInt32:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF)), (UInt8((self >> 16) & 0xFF)), (UInt8((self >> 24) & 0xFF))]
}
}
extension Packable {
func packed() -> [UInt8] {
let mirror = Mirror(reflecting:self)
var bytes:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as Packable:
bytes += value.packed()
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
return bytes
}
}
Being able to "pack" things is as easy adding them to the Packable protocol and telling them to pack themselves. For my cases above, I only need 3 different types of signed integers, but one could add lots more. For example, in my own code, I have some Enums derived from UInt8 which I added the packed method to.
extension Thing:Packable { }
extension Inner:Packable { }
var output = thing.packed()
output.count // --> 9 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de2211dd 13> :)
To be able to unpack stuff, I came up with a little bit of support:
protocol UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> Self
}
extension UInt8:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt8 {
return input.next()!
}
}
extension UInt16:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt16 {
return UInt16(input.next()!) | (UInt16(input.next()!) << 8)
}
}
extension UInt32:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt32 {
return UInt32(input.next()!) | (UInt32(input.next()!) << 8) | (UInt32(input.next()!) << 16) | (UInt32(input.next()!) << 24)
}
}
With this, I can then add initializers to my high level structures, e.g.
extension Inner:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(ai: UInt16.unpack(&bytes), bi: UInt8.unpack(&bytes))
}
}
extension Thing:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(a: UInt8.unpack(&bytes), b: UInt32.unpack(&bytes), inner: Inner(packed:&bytes), c: UInt8.unpack(&bytes))
}
}
What I liked about this is that these initializers call the default initializer in the same order and types as the structure is defined. So if the structure changes in type or order, I have to revisit the (packed:) initializer. The kids a bit long, but not too.
What I didn't like about this, was having to pass the inout everywhere. I'm honestly not sure what the value is of value based generators, since passing them around you almost always want to share state. Kind of the whole point of reifying an object that captures the position of a stream of data, is to be able to share it. I also don't like having to specify IndexingGenerator directly, but I imagine there's some fu magic that would make that less specific and still work, but I'm not there yet.
I did play with something more pythonic, where I return a tuple of the type and the remainder of a passed array (rather than a stream/generator), but that wasn't nearly as easy to use at the top level init level.
I also tried putting the static methods as extensions on byte based generators, but you have to use a function (would rather have used a computed var with side effects) there whose name doesn't match a type, so you end up with something like
self.init(a: bytes._UInt8(), b: bytes._UInt32(), inner: Inner(packed:&bytes), c: bytes._UInt8())
This is shorter, but doesn't put the type like functions next to the argument names. And would require all kinds of application specific method names to be added as well as one extended the set of UnpackablePrimitives.

Swift structs to NSData and back

I have a struct containing a struct and an NSObject that I want to serialize into an NSData object:
struct Packet {
var name: String
var index: Int
var numberOfPackets: Int
var data: NSData
}
var thePacket = Packet(name: name, index: i, numberOfPackets: numberOfPackets, data: packetData)
How do I best serialize the Packet into an NSData, and how do I best deserialize it?
Using
var bufferData = NSData(bytes: & thePacket, length: sizeof(Packet))
of only gives me the pointers of name and data. I was exploring NSKeyedArchiver, but then I'd have to make Packet an object, and I'd prefer to keep it a struct.
Cheers
Nik
Not really getting any feedback, this is the solution I ended up with:
Make encode() and decode() functions for my struct
Change Int to Int64 so the Int has the same size on 32-bit and 64-bit platforms
Have an intermediate struct (ArchivedPacket) that has no String or Data, but only Int64
Here is my code, I would be very grateful for your feedback, especially if there are less cumbersome ways to do this:
public struct Packet {
var name: String
var index: Int64
var numberOfPackets: Int64
var data: NSData
struct ArchivedPacket {
var index : Int64
var numberOfPackets : Int64
var nameLength : Int64
var dataLength : Int64
}
func archive() -> NSData {
var archivedPacket = ArchivedPacket(index: Int64(self.index), numberOfPackets: Int64(self.numberOfPackets), nameLength: Int64(self.name.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)), dataLength: Int64(self.data.length))
var metadata = NSData(
bytes: &archivedPacket,
length: sizeof(ArchivedPacket)
)
let archivedData = NSMutableData(data: metadata)
archivedData.appendData(name.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!)
archivedData.appendData(data)
return archivedData
}
func unarchive(data: NSData!) -> Packet {
var archivedPacket = ArchivedPacket(index: 0, numberOfPackets: 0, nameLength: 0, dataLength: 0)
let archivedStructLength = sizeof(ArchivedPacket)
let archivedData = data.subdataWithRange(NSMakeRange(0, archivedStructLength))
archivedData.getBytes(&archivedPacket)
let nameRange = NSMakeRange(archivedStructLength, Int(archivedPacket.nameLength))
let dataRange = NSMakeRange(archivedStructLength + Int(archivedPacket.nameLength), Int(archivedPacket.dataLength))
let nameData = data.subdataWithRange(nameRange)
let name = NSString(data: nameData, encoding: NSUTF8StringEncoding) as! String
let theData = data.subdataWithRange(dataRange)
let packet = Packet(name: name, index: archivedPacket.index, numberOfPackets: archivedPacket.numberOfPackets, data: theData)
return packet
}
}
Swift 5
If you are on Apple platforms, use Codable now. See documentation.
Swift 3
This is an unaltered copy-paste from a Playground in Xcode 8.2.1 that works. It is a bit simpler than other answers.
import Foundation
enum WhizzoKind {
case floom
case bzzz
}
struct Whizzo {
let name: String
let num: Int
let kind:WhizzoKind
static func archive(w:Whizzo) -> Data {
var fw = w
return Data(bytes: &fw, count: MemoryLayout<Whizzo>.stride)
}
static func unarchive(d:Data) -> Whizzo {
guard d.count == MemoryLayout<Whizzo>.stride else {
fatalError("BOOM!")
}
var w:Whizzo?
d.withUnsafeBytes({(bytes: UnsafePointer<Whizzo>)->Void in
w = UnsafePointer<Whizzo>(bytes).pointee
})
return w!
}
}
let thing = Whizzo(name:"Bob", num:77, kind:.bzzz)
print("thing = \(thing)")
let dataThing = Whizzo.archive(w: thing)
let convertedThing = Whizzo.unarchive(d: dataThing)
print("convertedThing = \(convertedThing)")
Notes
I couldn't make archive and unarchive instance methods because Data.init(bytes:​count:​) is mutating on the bytes parameter? And self isn't mutable, so... This made no sense to me.
The WhizzoKind enum is in there because that is something I care about. It's not important for the example. Someone might be paranoid about enums like I am.
I had to cobble this answer together from 4 other SO question/answers:
Getting data out of NSData with Swift
Extract struct from NSData in Swift
'bytes' is unavailable: use withUnsafeBytes instead
Unsafe bytes in Swift 3
And these docs:
- http://swiftdoc.org/v3.1/type/UnsafePointer/
And meditating on the Swift closure syntax until I wanted to scream.
So thanks to those other SO askers/authors.
Update
So this will not work across devices. For example, sending from iPhone 7 to Apple Watch. Because the stride is different. The above example is 80 bytes on iPhone 7 Simulator but 40 bytes on Apple Watch Series 2 Simulator.
It looks like the approach (but not syntax) by #niklassaers is still the only one that will work. I'm going to leave this answer here because it might help others with all the new Swift 3 syntax and API changes surrounding this topic.
Our only real hope is this Swift proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0166-swift-archival-serialization.md
The easiest way for basic struct objects is PropertyListEncoder & PropertyListDecoder.
This is the sample code;
Swift 5
struct Packet: Codable {
var name: String
var index: Int
var numberOfPackets: Int
var data: Data
}
func getDataFromPacket(packet: Packet) -> Data?{
do{
let data = try PropertyListEncoder.init().encode(packet)
return data
}catch let error as NSError{
print(error.localizedDescription)
}
return nil
}
func getPacketFromData(data: Data) -> Packet?{
do{
let packet = try PropertyListDecoder.init().decode(Packet.self, from: data)
return packet
}catch let error as NSError{
print(error.localizedDescription)
}
return nil
}
I used Jeff's example to create the following struct:
struct Series {
var name: String?
var season: String?
var episode: String?
init(name: String?, season: String?, episode: String?) {
self.name = name
self.season = season
self.episode = episode
}
static func archive(w: Series) -> Data {
var fw = w
return Data(bytes: &fw, count: MemoryLayout<Series>.stride)
}
static func unarchive(d: Data) -> Series {
guard d.count == MemoryLayout<Series>.stride else {
fatalError("Error!")
}
var w: Series?
d.withUnsafeBytes({(bytes: UnsafePointer<Series>) -> Void in
w = UnsafePointer<Series>(bytes).pointee
})
return w!
}
}
Like Dag mentioned the whole thing is a bit fragile. Sometimes the App crashes when the name contains whitespace or an underline/underscore, and sometimes it crashes just without reason. In all cases the name which is unarchived looks similar to this '4\200a\256'. Surprisingly this is not a problem in the case of season or episode (like in "Season 2"). Here the whitespace doesn't force the app to crash.
Maybe it's an alternative to encode the strings to utf8 but I'm not familiar enough with the archive/unarchive methods to adapt them for this case.
It seems this came out recently, and to me it is looking solid. Didn't try it yet...
https://github.com/a2/MessagePack.swift
Well, Swift doesn't have any magical serialization method, if that's what you are after. Since the good days of C, when you have a struct with a pointer, that is a flag that you can't serialize the bytes of that struct's instance without following the pointers and fetching their data. Same applies to Swift.
Depending on your Serialization needs and constraints, I'd say using NSCoding or even JSON strings will tidy your code and make it more predictable than the current state. Sure, you'll need to write a mapper, and there is an overhead. Everyone will tell you this: "Measure first".
Now, here is the interesting part:
If you really want to inline your data in that struct, and stream the contents without building the packet around NSData as you're doing, you can reserve bytes using Swift Tuples, which work much like how you would reserve bytes in C using char[CONST]:
struct what {
var x = 3
}
sizeof(what)
$R0: Int = 8
struct the {
var y = (3, 4, 5, 7, 8, 9, 33)
}
sizeof(the)
$R1: Int = 56
To expand a bit on this, I think it's pretty horrible, but possible. You can write to the tuple's memory location and read from it using something like this.