0

I can't figure out why the below code is not working :

import Foundation
import SpriteKit

var TestVector: CGVector = CGVector(dx: 1, dy: 1)

var Data: Dictionary<String, Any> = ["abc":CGPoint(x: 0, y: 0), "def":1, "ghi":"Try", "jkl":TestVector]


let DataConverted: Data = NSKeyedArchiver.archivedData(withRootObject: Data)

let DataReceived: Dictionary = (NSKeyedUnarchiver.unarchiveObject(with: DataConverted) as! [String : Any])

Error throws is : error: Execution was interrupted, reason: signal SIGABRT. The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

Everything is working fine for a lot of different types of data (String, Int, Double,...) but it is not working for vectors

Are there limitations in the type of data that can be archived via NSKeyedArchiver ?

Thx

J.

Additional info : this is to be used in a MultipeerConnectivy data transfer

Jonah Begood
  • 317
  • 2
  • 14
  • `CGPoint` shouldn't default can't be converted as such into `(NS)Data`, so it can't be archived. Same for `TestVector`. You need to conform it to `NSCoder` protocol. – Larme Oct 13 '17 at 13:34
  • Thx. It is however working for CGPoint. Only CGVector type throws an error. Would you be able to tell how to conform CGPoint and CGVector to NSCoder protocol and what would be the best syntax ? – Jonah Begood Oct 13 '17 at 13:39
  • 2
    @Larme You mean `NSCoding` – Leo Dabus Oct 13 '17 at 13:40
  • @LeoDabus Yes, `NSCoding`. For @jbegood: https://stackoverflow.com/questions/37980432/swift-3-saving-and-retrieving-custom-object-from-userdefaults – Larme Oct 13 '17 at 13:42

1 Answers1

1

Check out NSValue, it is perfect in this scenario.

Add it like NSValue(cgVector: TestVector) and when you are reading it back from dictionary just get it back as CGVector like so let vector = cgVectorValue.cgVectorValue

In your case you do:

var TestVector: CGVector = CGVector(dx: 1, dy: 1)
let vectorValue = NSValue(cgVector: TestVector)

let pointValue = NSValue(cgPoint:CGPoint(x: 0, y: 0))

var Data: Dictionary<String, Any> = ["abc":pointValue, "def":1, "ghi":"Try", "jkl":vectorValue]

let DataConverted: Data = NSKeyedArchiver.archivedData(withRootObject: Data)

let DataReceived: Dictionary = (NSKeyedUnarchiver.unarchiveObject(with: DataConverted) as! [String : Any])

if let vectorValue = DataReceived["jkl"] as? NSValue {
    let vector = vectorValue.cgRectValue
}

if let pointValue = DataReceived["abc"] as? NSValue {
    let point = vectorValue.cgPointValue
}

Apple uses this all the time, maybe you have seen it when you get a notification for keyboard will show, inside of userInfo dictionary you can get to keyboard size using

CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size

Notice the CGRectValue, get CGRect that was encapsulated using NSValue

From Apple's docs:

An NSValue object can hold any of the scalar types such as int, float, and char, as well as pointers, structures, and object id references. Use this class to work with such data types in collections (such as NSArray and NSSet), Key-value coding, and other APIs that require Objective-C objects. NSValue objects are always immutable.

More on https://developer.apple.com/documentation/foundation/nsvalue

Ladislav
  • 7,223
  • 5
  • 27
  • 31
  • Thx but it is not working. Error is : Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSKeyedArchiver encodeValueOfObjCType:at:]: this archiver cannot encode structs' On the line "let DataConverted..." – Jonah Begood Oct 14 '17 at 14:13
  • I updated the answer, you also had a CGPoint there, you wrap that in NSValue as well as you can see above and then it must work – Ladislav Oct 14 '17 at 15:25
  • I would tend to agree with others that commented about creating a custom Class that conforms to NSCoding protocol... – Ladislav Oct 14 '17 at 15:27