Bruno Morgado
Bruno Morgado

Reputation: 507

How to know the type of the object boxed in NSValue with Swift?

I have an NSValue object that can "box" an instance of an unknown type (CGPoint, CGRect, etc.) and I need to determine this type in runtime. How would I do it in Swift?

I tried to do something like this:

if ((value as Any) is CGPoint) {}
else if (((value as Any) is CGRect)) {}
...

When value is a NSValue object containing a CGPoint, it does not get into the if clause. Then I when I printed value it gives me NSPoint: {150, 150} which I assume is why it never gets in the clause.

Any ideas why this happens and how to solve it?

Thanks a lot!

Upvotes: 2

Views: 1741

Answers (2)

Airspeed Velocity
Airspeed Velocity

Reputation: 40963

Ah, NSValue, you so crazy. Even though NSRect etc aren't supposed to be a thing on iOS, if you put a CGRect inside an NSValue, internally it displays it has an NSRect inside it.

Anyway, while they serve similar purposes I don't think you can use Any to convert an NSValue to or from a CGPoint or CGRect. You have to call value.CGRectValue() etc. But to your point, you need to know which it is. Which also isn't easy, but you can do this:

let type = String.fromCString(val.objCType) ?? ""
if type.hasPrefix("{CGRect") {
    let rect = val.CGRectValue()
}
else if type.hasPrefix("{CGPoint") {
    let point = val.CGPointValue()
} //etc...

But another question would be, do you really need to use NSValue? It's kind of an Obj-C specific workaround. Is it being given to you by something you can’t change or might you be better off using Any (or even better, generics) instead?

Upvotes: 4

David Berry
David Berry

Reputation: 41236

To start with, iOS doesn't use NSPoint, so that would be a loss. My best guess for something like this would be to fall back to using objCType, which is how it would be handled in objective-C. Unfortunately, you don't have access to @encode from swift, so you wind up having to hardcode the possibilities:

switch String(CString: value.objCType, encoding: NSUTF8StringEncoding) {
case "{CGPoint=dd}":  // handle CGPoint here
case "{CGRect={CGPoint=dd}{CGSize=dd}}": // handle CGRect here
}

Upvotes: 2

Related Questions