How to Represent Dictionary as NSValue in Swift 3? - swift

I have the following Dictionary:
let example: [String: (Int, Int)] = ["test": (0, 1)]
I need to store this as an NSData variable, and to do that it must first be converted to an NSValue, which I try to do as follows:
let nsval = NSValue().getValue(example as! UnsafeMutableRawPointer)
Only to be met with the error:
Cannot convert value of type 'Void' (aka '()') to specified type 'NSValue'
I've seen SO answers that suggest using:
let test = UnsafeMutablePointer.load(example)
But that also yields the error:
Type 'UnsafeMutablePointer<_>' has no member 'load'
So then, how is one to convert a dictionary to an NSValue in Swift 3?

not all swift types are compatible with Foundation classes. So you cannot construct an NSValue containing the type [String: (Int, Int)] you can, however start out with an NSDictionary. Ditch the tuple and choose a type thats compatible with objc. then you can change type of let example: to NSDictionary. use that with the NSValue methods.
That said, if you're trying to turn general objc types into an NSData for general serialization purposes, you're going to get more milage out of the NSKeyedArchiver/NSKeyedUnarchiver methods like .archivedData(withRootObject: Any) -> Data (though they still don't support tuples.)

In theory, if you really want to turn a swift Tuple into a data object, you could do this.
var x:(Int, Int) = (4, 2)
var bytes:Data = withUnsafeBytes(of: &x){ g in
var d = Data()
for x in g {
d.append(x)
}
return d
}
heres the reverse:
var y:(Int, Int) = (0,0)
bytes.withUnsafeBytes { (x:UnsafePointer<(Int, Int)>) in
y = x.pointee
}
print(y) // prints (4, 2)
Beware: this is the path to the dark side.

Related

Why casting function type as AnyObject works

Question : Difference between Any vs. AnyObject
Answer :
Any can represent an instance of any type at all, including function types and optional types.
AnyObject can represent an instance of any class type.
I tried to store a function type in a Any and a AnyObject variables
func add(a: Int, b: Int) -> Int {
return a + b
}
let funcType = add
let test1: Any = funcType
let test2: AnyObject = funcType//Value of type '(Int, Int) -> Int' does not conform to specified type 'AnyObject' Insert ' as AnyObject'
When I use the fix option
let test2: AnyObject = funcType as AnyObject
It works without any error. How am I able to store a function type in a AnyObject?
Behind the scenes as AnyObject converts the casted value to an Objective-C compatible one: Int's become NSNumber, array's become NSArray, and so on. Swift-only values get wrapped within opaque SwiftValue instances.
print(type(of: test2)) // __SwiftValue
This is why adding as AnyObject makes your code compile, as the right-hand of the operator is now an object.

Can you overload type-casting operators in Swift?

I'd like to implement automatic type conversions between known types in Swift. The C# way of doing it has been overloading type-casting operators. If I want my X type to be cross-assignable with, say, string, I would write:
public class X
{
public static implicit operator string(X value)
{
return value.ToString();
}
public static implicit operator X(string value)
{
return new X(value);
}
}
After that I could write stuff like:
string s = new X();
X myObj = s;
and they would be automatically converted. Is that possible in any way in Swift?
No, the language doesn't provide such functionality for custom types. There is bridging between Objective-C collections and Swift collections but that's baked in and not customizable.
// Swift array of `String` elements
let swiftArray: [String] = ["Bob", "John"]
// Obj-C array of `NSString` elements, but element type information
// is not known to the compiler, so it behaves like an opaque NSArray
let nsArray: NSArray = ["Kate", "Betty"]
// Obj-C array of an `NSString` and an `NSNumber`, element type
// information is not known to the compiler
let heterogeneousNSArray: NSArray = ["World", 3]
// Casting with `as` is enough since we're going from Swift array to NSArray
let castedNSArray: NSArray = swiftArray as NSArray
// Force casting with `as!` is required as element type information
// of Obj-C array can not be known at compile time
let castedSwiftArray: [String] = nsArray as! [String]
// Obj-C arrays can not contain primitive data types and can only
// contain objects, so we can cast with `as` without requiring a
// force-cast with `!` if we want to cast to [AnyObject]
let heterogeneousCastedNSArray: [AnyObject] = heterogeneousNSArray as [AnyObject]
Documentation for type casting is available here.
I think you can achieve what you want to do with initializers.
extension X {
init(string: String) {
self = X(string)
}
}
extension String {
init(x: X) {
// toString is implemented elsewhere
self = x.toString
}
}
let x = X()
let string = "Bobby"
let xFromString: X = X(string: string)
let stringFromX: String = String(x: x)
Not directly related to your question but there is also a family of protocols that start with ExpressibleBy..., enabling you to do things like the following:
Let's say we want to initialize strings from integer literals. We can do that by conforming to and implementing ExpressibleByIntegerLiteral
// Strings can not be initialized directly from integer literals
let s1: String = 3 // Error: Can not convert value of type 'Int' to specified type 'String'
// Conform to `ExpressibleByIntegerLiteral` and implement it
extension String: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
// String has an initializer that takes an Int, we can use that to
// create a string
self = String(value)
}
}
// No error, s2 is the string "4"
let s2: String = 4
A nice use case for ExpressibleByStringLiteral can be found here.

"Deep-copy" of Swift dictionary with map()?

I have a GKGameModel that stores its internal state in an array a of Cards and a dictionary b that maps from Ints to arrays of Cards. GameplayKit mandates that I must copy this internal state in setGameModel:.
The following code is meant to just-copy the array and "deep-copy" the dictionary. FWIK this should be sufficient since Cards themselves never change.
var a: [Card]
var b: [Int: [Card]]
func setGameModel(gameModel: GKGameModel) {
let otherGameModel = gameModel as! GameModel
a = otherGameModel.a
b = otherGameModel.b.map { (i: Int, cards: [Card]) in (i, cards) }
}
However, this causes the following syntax error in the line that attempt the "deep-copy":
Cannot assign a value of type '[(Int, [Card])]' to a value of type
'[Int, [Card]]'.
What am I doing wrong?
In your case:
b = otherGameModel.b
is sufficient.
Because, Array and Dictionary are both value types. So when it is assigned to another variable, it will be deep copied.
var bOrig: [Int: [Int]] = [1: [1,2,3], 2:[2,3,4]]
var bCopy = bOrig
bCopy[1]![2] = 30
bOrig[1]![2] // -> 3
bCopy[1]![2] // -> 30
The error message reveals there is a type mismatch:
variable b is declared as Dictionary<Int,[Card]> but the map function returns an Array of tuplets (Int, [Card])

Swift [1,2] conforms to AnyObject but [Enum.a, Enum.b] does not

I'm in AppDelegate, trying to pass a reply to a WatchKit Extension Request. I cannot use an array of enums as the value in a Dictionary whose values are typed as AnyObject. Experimenting in a Playground shows this:
enum E : Int {
case a = 0
case b
}
var x : AnyObject = [0, 1] // OK
var y : AnyObject = [E.a, E.b] // [E] is not convertible to AnyObject
Of course I can work around this by converting my enums to strings or numbers, but why is this a type error in Swift?
AnyObject exists for compatibility with Objective-C. You can only put objects into an [AnyObject] array that Objective-C can interpret. Swift enums are not compatible with Objective-C, so you have to convert them to something that is.
var x: AnyObject = [0, 1] works because Swift automatically handles the translation of Int into the type NSNumber which Objective-C can handle. Unfortunately, there is no such automatic conversion for Swift enums, so you are left to do something like:
var y: AnyObject = [E.a.rawValue, E.b.rawValue]
This assumes that your enum has an underlying type that Objective-C can handle, like String or Int.
Another example of something that doesn't work is an optional.
var a: Int? = 17
var b: AnyObject = [a] // '[Int?]' is not convertible to 'AnyObject'
See Working with Cocoa Data Types for more information.

Swift: How to better format the output when using println with a tuple?

I have defined this function:
func need_rebalance() -> (Bool, RebalanceStrategy) {
}
where RebalanceStrategy is an enum type
enum RebalanceStrategy: String {
case LeftRight = "LeftRight"
case RightLeft = "RightLeft"
}
When I tried to print it this way,
println("Need rebalance? \(self.need_rebalance())")
I got output like this:
Need rebalance? (false, (Enum Value))
My questions are:
1) Is there an easy to extract a value from a tuple? (Hopefully something similar to python e.g. self.need_rebalance()[1]. Apparently this syntax does not work in swift because tuple does not support subscript())
2) How can I print the raw value of enum instead of having (Enum Value)?
I am using XCode6 Beta5
There's a way to extract the value using tuple indexes, but it's not nice, it involves reflect:
let tuple = self.need_rebalance()
let reflection = reflect(tuple)
reflection[0].1.value // -> true
reflection[1].1.value // -> RebalanceStrategy.?
Also, if your tuple members are not named:
let tuple = self.need_rebalance()
tuple.0 // -> true
tuple.1 // -> RebalanceStrategy.?
To access the raw value in an enum:
RebalanceStrategy.LeftRight.toRaw()
Use .0, .1 and so on to get the respective value from an unnamed tuple.
To get the raw value of the enum, use .toRaw()
var tuple = self.need_rebalance()
println("Need rebalance? \(tuple.0),\(tuple.1.toRaw())")
Better still, use a named tuple like this:
var tuple : (boolValue : Bool, enumValue :RebalanceStrategy) = self.need_rebalance()
println("Need rebalance? \(tuple.boolValue),\(tuple.enumValue.toRaw())")