Does not have a member named 'subscript' - swift

I'm building something and it all worked fine until Swift 1.2 came out. I made some changes but still have one line of code that is playing nice. I don't understand why this is breaking:
let swiftArray = positionDictionary.objectForKey["positions"] as? [AnyObject]
it gives me an error:
'(AnyObject) -> AnyObject?' does not have a member named 'subscript'
I also tried using this:
let swiftArray = positionDictionary.objectForKey?["positions"] as? [AnyObject]
but then I get an error saying:
Operand of postfix '?' should have an optional type; type is '(AnyObject) -> AnyObject?'
I'm really confused...can anyone help?
func addOrbsToForeground() {
let orbPlistPath = NSBundle.mainBundle().pathForResource("orbs", ofType: "plist")
let orbDataDictionary : NSDictionary? = NSDictionary(contentsOfFile: orbPlistPath!)
if let positionDictionary = orbDataDictionary {
let swiftArray = positionDictionary.objectForKey["positions"] as? [AnyObject]
let downcastedArray = swiftArray as? [NSArray]
for position in downcastedArray {
let orbNode = Orb(textureAtlas: textureAtlas)
let x = position.objectForKey("x") as CGFloat
let y = position.objectForKey("y") as CGFloat
orbNode.position = CGPointMake(x,y)
foregroundNode!.addChild(orbNode)
}
}

positionDictionary is an NSDictionary. You can use it just like a Swift dictionary - you don't need to use objectForKey.
You should just use if let and optional casting to get the value you want, which I think is an array of NSDictionary since you're using objectForKey again later:
if let downcastedArray = positionDictionary["positions"] as? [NSDictionary] {
for position in downcastedArray {
let orbNode = Orb(textureAtlas: textureAtlas)
let x = position["x"] as CGFloat
let y = position["y"] as CGFloat
orbNode.position = CGPointMake(x,y)
foregroundNode!.addChild(orbNode)
}
}
As a side note, CGPointMake is not stylistically preferred in Swift. Instead, consider using the CGPoint initializer:
orbNode.position = CGPoint(x: x, y: y)

Related

Unable to bridge NSNumber to Float

I'm runtime errors on my existing project when migrating versions of Xcode. Can someone help with this? Is my typecasting wrong?
Code:
let x = d[ChildItem.KEY_LOOK] as! NSArray as Array
let y = d[ChildItem.KEY_COOR] as! NSArray as Array
let item = ChildItem(x as! [Float], y as! [Float])
Error:
Thread 5: Fatal error: Unable to bridge NSNumber to Float
I know how to cast an NSNumber to a Float as I found on SO (Unable to bridge NSNumber to Float in JSON parsing):
if let n = d.value(forKey: "probability") as? NSNumber {
let f = n.floatValue }
How can I do this for an NSNumber array?
In each of the d[] keys there are JSON strings like this:
d[ChildItem.KEY_LOOK] = [465918.2, 5681518.0,4462.3203]
you need to use the floatValue property of NSNumber, not casting directly.
Let's say you have an array of numbers you got from the server calls like this
d[ChildItem.KEY_LOOK] = [465918.2, 5681518.0,4462.3203] // from your example
you can do the following
//As from your example that can be directly convert to Array of Doubles like this
if let arrayOfDoubles = d[ChildItem.KEY_LOOK] as? [Double] {
debugPrint(arrayOfDoubles)
}
And if for some reason you want those to be [NSNumber] then you can do like this
if let arrayOfNumbers = d[ChildItem.KEY_LOOK] as? [NSNumber] {
//to make this array of double
let doubleArray = arrayOfNumbers.map { $0.doubleValue }
//to make int
let intArray = arrayOfNumbers.map { $0.intValue }
... and so on
}

Is there a better way to get numbers from dictionary (JSON data)?

Assume I receive data in some JSON which is then parsed to dictionary using a native tool. Some of those values are numbers and naturally I will need to parse them, cast them into what I need in the application.
But the transition from Any to an actual number such as Float, Int, Double seems to be a bit messy. For instance if I expect a double but at some point server returns an integer my code will fail using:
let doubleValue = dictionary["key"] as! Double
So this will work when the item is 1.3 but will fail for 1. To use a bit more concrete example we can use the following:
let myDictionary: [String: Any] = [
"myNumber_int" : 1,
"myNumber_float" : Float(1),
"myNumber_cgFloat" : CGFloat(1),
"myNumber_double" : 1.0
]
let numberInt1 = myDictionary["myNumber_int"] as? Int // 1
let numberInt2 = myDictionary["myNumber_int"] as? Float // nil
let numberInt3 = myDictionary["myNumber_int"] as? CGFloat // nil
let numberInt4 = myDictionary["myNumber_int"] as? Double // nil
let numberFloat1 = myDictionary["myNumber_float"] as? Int // nil
let numberFloat2 = myDictionary["myNumber_float"] as? Float // 1
let numberFloat3 = myDictionary["myNumber_float"] as? CGFloat // nil
let numberFloat4 = myDictionary["myNumber_float"] as? Double // nil
let numberCGFloat1 = myDictionary["myNumber_cgFloat"] as? Int // nil
let numberCGFloat2 = myDictionary["myNumber_cgFloat"] as? Float // nil
let numberCGFloat3 = myDictionary["myNumber_cgFloat"] as? CGFloat // 1
let numberCGFloat4 = myDictionary["myNumber_cgFloat"] as? Double // nil
let numberDouble1 = myDictionary["myNumber_double"] as? Int // nil
let numberDouble2 = myDictionary["myNumber_double"] as? Float // nil
let numberDouble3 = myDictionary["myNumber_double"] as? CGFloat // nil
let numberDouble4 = myDictionary["myNumber_double"] as? Double // 1
So for each type only 1 cast will actually work which is... well I would at least expect that CGFloat will be able to cast directly to Double or Float...
So my solution is using NSNumber:
let myNumbers: [CGFloat] = [
CGFloat((myDictionary["myNumber_int"] as? NSNumber)?.floatValue ?? 0.0),
CGFloat((myDictionary["myNumber_float"] as? NSNumber)?.floatValue ?? 0.0),
CGFloat((myDictionary["myNumber_cgFloat"] as? NSNumber)?.floatValue ?? 0.0),
CGFloat((myDictionary["myNumber_double"] as? NSNumber)?.floatValue ?? 0.0),
] // [1,1,1,1]
This naturally works but the code is pretty ugly for something as simple as this. But putting the code "ugliness" aside; can we not do similar without using Next Step? I mean NSNumber for something as seemingly trivial as this? I am missing something very obvious here, right?
Try like this:-
if let data = myDictionary as? [String: AnyObject] {
let myNumber = data["myNumber_int"] as? Int
}

How to add and retrieve a CGpoint value into NSmutable dictionary in swift

This is my code
let point: CGPoint = CGPoint(x:10,y:10) let ns = NSValue(CGPoint: point)
let dict:NSMutableDictionary = ["point":"\(ns)"]
let getVal:CGPoint = (dict["point"] as? NSValue)!.CGPointValue()
The better would be using Swift Dictionary rather than NSMutableDictionary. But if you have some reason you need to use NSMutableDictionary, you can store CGPoints in a NSMutableDictionary like this.
let point: CGPoint = CGPoint(x:10, y:10)
let ns = NSValue(CGPoint: point)
let dict:NSMutableDictionary = ["point": ns] //<- You should not use `"\(ns)"` hear!
if let val = dict["point"] as? NSValue {
let cgPointVal = val.CGPointValue()
print(cgPointVal) //->(10.0, 10.0)
}
let point: CGPoint = CGPoint(x:10,y:10)
let dict:NSMutableDictionary = ["point":NSValue(CGPoint: point)]
let getVal:CGPoint = (dict["point"] as? NSValue)!.CGPointValue()
print(getVal)
Maybe something like this
let point = CGPoint(x:10,y:10)
let point2 = NSPoint(x:20,y:10)
var dict = [String : NSPoint ]()
dict["point"] = point
dict["point2"] = point2
print(dict["point"])
print(dict["point2"])
You can use CGPoint and NSPoint interchangeably in swift (NSPoint is OSX only as #OOPer pointed out)

Cast from '[NSObject : AnyObject]?' to unrelated type 'NSDictionary' always fails

This line let userInfo = notification.userInfo as! NSDictionary I get a warning: Cast from '[NSObject : AnyObject]?' to unrelated type 'NSDictionary' always fails
I try to use let userInfo = notification.userInfo as! Dictionary<NSObject: AnyObject> replace let userInfo = notification.userInfo as! NSDictionary. But I get an error :Expected '>' to complete generic argument list. How to fix the warning.
Xcode 7.1 OS X Yosemite
This is my code:
func keyboardWillShow(notification: NSNotification) {
let userInfo = notification.userInfo as! NSDictionary //warning
let keyboardBounds = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()
let duration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber).doubleValue
let keyboardBoundsRect = self.view.convertRect(keyboardBounds, toView: nil)
let keyboardInputViewFrame = self.finishView!.frame
let deltaY = keyboardBoundsRect.size.height
let animations: (()->Void) = {
self.finishView?.transform = CGAffineTransformMakeTranslation(0, -deltaY)
}
if duration > 0 {
} else {
animations()
}
}
NSNotification's userInfo property is already defined as a(n optional) dictionary.
So you don't need to cast it at all, just unwrap it.
func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
...
}
}
all the rest of your code should work as is.
You are trying to force cast an optional to an NSDictionary. Try:
let userInfo = notification.userInfo! as NSDictionary
This worked for me.

CMFormatDescription to CMVideoFormatDescription

I'm trying to get the resolution of the camera of a device using swift.
I'm using CMVideoFormatDescriptionGetDimensions which requires a CMVideoFormatDescription, but AVCaptureDevice.formatDescription returns a CMFormatDescription. I've tried a multitude of ways to cast CMFormatDescription to CMVideoFormatDescription and can't seem to get it working.
Below is a sample of the code that I'm using:
for format in device.formats as [AVCaptureDeviceFormat] {
let videoDimensions = CMVideoFormatDescriptionGetDimensions(format.formatDescription)
}
This doesn't seem possible in Swift at the moment. One solution then would be to write a helper function in objective-c, such as:
CMVideoDimensions CMFormatDescriptionGetDimensions(CMFormatDescriptionRef formatDescription)
{
if (CMFormatDescriptionGetMediaType(formatDescription) == kCMMediaType_Video)
return CMVideoFormatDescriptionGetDimensions(formatDescription);
else
return (CMVideoDimensions) {
.width = 0,
.height = 0
};
}
Include the header with the function prototype in the Swift bridging header so that it will be accessible as a global function from your Swift code.
I was able to get the resolution using the swift method below:
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) as AVCaptureDevice
let formatDesc = captureDevice.activeFormat.formatDescription
let dimensions = CMVideoFormatDescriptionGetDimensions(formatDesc)
Here's a solution in pure Swift, really only usable for logging purposes and such. Paste the following function in your class or somewhere else:
func widthAndHeightFromTrack(track: AVAssetTrack) -> CGSize {
let str = track.formatDescriptions.description
let regex = try! NSRegularExpression(pattern: "[0-9]{2,4} x [0-9]{2,4}", options: [])
if let result = regex.firstMatchInString(str, options: [], range: NSMakeRange(0, str.characters.count)) {
let dimensionString = (str as NSString).substringWithRange(result.range)
let dimensionArray = dimensionString.componentsSeparatedByString(" x ")
let width = Int(dimensionArray[0])
let height = Int(dimensionArray[1])
return CGSize(width: width!, height: height!)
}
return CGSizeZero
}
Example usage:
let allTracks: AVAsset = someAVAsset.tracksWithMediaType(AVMediaTypeVideo)
let videoTrack = allTracks[0]
let videoTrackDimensions = widthAndHeightFromTrack(videoTrack)
// You now have a CGSize, print it
print("Dimensions: \(videoTrackDimensions)")
Of course, the above solution will completely break whenever Apple changes something in the string representation of the CMFormatDescription. But it's useful for logging the dimensions.
Maybe, question is too old, but the Swift issue is still not fixed.
public extension AVURLAsset {
var audioFormatDescription: CMAudioFormatDescription? {
if let track = self.tracks(withMediaType: .audio).first,
let untypedDescription = track.formatDescriptions.first {
// hacks, warnings, disablings of swiftlint below are wrork-around of
// Swift bug: it fails converting 'Any as CMFormatDescription'
let forceTyped: CMFormatDescription?
//swiftlint:disable force_cast
= untypedDescription as! CMAudioFormatDescription
//swiftlint:enable force_cast
if let description = forceTyped {
return description
} else {
return nil
}
} else {
return nil
}
}
}