I want to know how to create a dictionary of [String:AnyObject] from an UnsafePointer<AudioStreamBasicDescription>
I guess I don't understand how to work with an UnsafePointer<T> in Swift. Here's where I'm starting from - The AVAudioFile class has a fileFormat property which is of AVAudioFormat Type. AVAudioFormat has a streamDescription property which returns an UnsafePointer<AudioStreamBasicDescription> as a read-only property.
I'd like to see what the values are in this struct and converting to a Dictionary seems like it might be reasonable goal. In fact, there already seems to be a "settings" property on the AVAudioFormat Class that does this but I'm interested in understanding the "right way" to access the values stored in the UnsafePointer myself.
From the docs
Discussion: Returns the AudioStreamBasicDescription (ASBD) struct, for use with lower-level audio APIs
https://developer.apple.com/library/prerelease/ios/documentation/AVFoundation/Reference/AVAudioFormat_Class/index.html
Is there a way to do an unsafe conversion after checking if the struct is not nil? Would I use an unsafeBitCast here? I'm hesitant to jump into this too much as I've read that it's "extremely dangerous"...
I realize I can access the underlying memory with the following:
let audioFileURL:NSURL = NSBundle.mainBundle().URLForResource("FILENAME", with Extension: "mp3")
var file:AVAudioFile?
do {
file = try AVAudioFile(forReading: audioFileURL)
guard let file = file else {
fatalError("file must not be nil")
}
}catch {
print(error)
}
let format = file.processingFormat
let asbd:AudioStreamBasicDescription = format.streamDescription.memory
Is that dangerous and would I need to dealloc for some reason after creating the asbd constant?
I've tried to follow along with the post here http://sitepoint.com/using-legacy-c-apis-swift but I'm just not getting it... Looking for any direction on best practices here.
Update:
Doing a bit more research, it seems that it might be possible to create the dictionary using reflect based off this post: http://freecake.angelodipaolo.org/simple-reflection-in-swift/
Here's what I have so far:
let asbdMirror = reflect(asbd)
var asbdDict = [String: Any]()
for i in 0..<asbdMirror.count {
let (propertyName, childMirror) = asbdMirror[i]
asbdDict[propertyName] = childMirror.value
}
Any reason this is a bad idea?
You are doing everything correctly, you can access all the values in the description with asbd.mSampleRate and such. It wouldn't make sense to convert it to a dictionary because that's just not what it is, there are no keys for the values.
You also don't have to dealloc anything when working with pointers like this unless you allocate one yourself (when using malloc or alloc)
Related
I have no idea why I get this error.
The problem code is here
for i in 0..<itemDataJson?.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
When I print(itemDataJson?.count) it prints Optional(1).
What am I doing wrong?
Thank you.
It's printing Optional(1) because the variable itemDataJson is nullable, so the count would therefore have to be nullable, because we don't know if itemDataJson actually has a value.
The main problem that I see in your code is that you are force-unwrapping variables. Force-unwrapping a variable is a code smell (usually, although I do it myself from time to time, but you need to be careful).
When you force unwrap a variable, you need to ask yourself the question, "Do I want the app to crash here if this variable is nil?". If the answer is yes, then using a force unwrap is acceptable, otherwise, you should create a non-nullable variable or if that is not possible, you should use the guard statement in swift.
This could be used like this:
guard let itemDataJson = itemDataJson else {
// itemDataJson was null do something
return
}
You can use the if let construct as well, but be careful of the pyramid of doom if you don't use the if let construct correctly. See here for using it correctly, or use a guard statement.
I would recommend checking out the documentation on optionals if you have not done so already.
I would change the code to this version:
if (itemDataJson != nil) {
for i in 0..<itemDataJson!.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}
You should check all optionals before you try to access the underlying value. Best course of action would be to use if let statements.
if let itemDataJSON = itemDataJSON {
for i in 0..<itemDataJSON.count {
if let items = appDelegate.itemDataJSON {
if let imageURL = items[i]["imageURL"].string {
imageUrls.append(imageURL)
}
}
}
}
Other than that, it's a bad practice to store data in AppDelegate. You should create your own data layer that is not dependent of AppDelegate to make data handling more efficient and easier.
instead of checking for nil you should try this.
if let item = itemDataJson {
for i in 0..<item.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}
I am trying to make a dictionary with the properties of a class of mine.
class SomeClass() {
var someString = "Hello, stackoverflow"
var someInt = 42 // The answer to life, the universe and everything
var someBool = true
func objectToDict() -> [String: String] {
var dict = [String: String]()
let reflection = Mirror(reflecting: self)
for child in reflection.children {
if let key = child.label {
dict[key] = child.value as? AnyObject
}
return dict
}
}
but objectToDict() is very slow. Is there a way to speed this up, or may be another approach to add the property values to a Dictionary?
I do not agree with most other users. Using reflection results less code, which means less time to develop, maintain, and test your product. With a well written library like EVReflection you don't need to worry about things behind the scene too much.
However, if performance is going to be a concern, do NOT use reflection based approaches at all. I'd say it's never really a problem in front-end development for me, especially in iOS, but it cannot be ignored in back-end development.
To see how slow it can be, I ran some test in Xcode. I'll write a blog about it, but generally speaking, getting Mirror is not the worst part (plus it may be possible to catch property list in memory), so replacing it with objc runtime wouldn't change the situation too much. In the other hand, setValue(_, forKey) is surprisingly slow. Considering that in real life you also need to perform tasks like checking dynamicType and so on, using the dynamic approach surely will make it 100+ times slower, which won't be acceptable for server development.
- Looping 1,000,000 times for a class with 1 `Int` property
- Getting mirror: 1.52
- Looping throw mirror and set `child.value`: 3.3
- Looping throw mirror and set `42`: 3.27
- Setting value `42`: 0.05
Again, for iOS I'll keep using it to save my time. Hopefully end customers won't care about whether it's 0.005 seconds or 0.0005 seconds.
Not only is that slow, it's also not a good idea: mirroring is for debug introspection only. You should instead construct the dictionary yourself. This ensures that you have the flexibility to store all the data in exactly the right way, and also decouples your Swift property names from the keys of the dictionary you're generating.
class SomeClass {
var someString = "Hello, stackoverflow"
var someInt = 42 // The answer to life, the universe and everything
var someBool = true
func objectToDict() -> [String: AnyObject] {
return ["someString": someString, "someInt": someInt, "someBool": someBool]
}
}
This is my first time using NSCoder and there used to be a method called encodeInteger but it seems to have vanished in Swift 3 and the docs don't seem to help.
It maybe that the confusion lies in the difference between Int and UInt64. Are they the same?
Should i be using a NSKeyedArchiver and if so how does that work to comply with NSCoding?
Here's before with the error:
And after without an error:
Why don't you use NSNumber and encode it as an object? It'd look like this:
let bigNumber: UInt64 = /* 123 */
let number = NSNumber(value: bigNumber)
// Encoding it just like a String
coder.encode(number, forKey: "BigNumberKey")
// Decoding and using the property uint64Value from NSNumber to get the UInt64 back
if let object = coder.decodeObject(forKey: "BigNumberKey") as? NSNumber {
let decodedBigNumber = object.uint64Value
}
If that's a requirement for some reason, NSCoder supports the encoding of Int64 (and you could cast it, described here).
The change from encodeInteger to just encode is part of SE-0005 (which affected a lot of different classes; UIColor.blueColor() is now UIColor.blue(), for instance).
From a server I receive a JSON string, then I try to convert it to an NSDictionary this way:
let JSON = try NSJSONSerialization.JSONObjectWithData(rToData!, options:[])
guard let JSONDictionary:NSDictionary = (JSON as! NSDictionary) else {
print("My grandma is way more NSDictionary than this")
return
}
Once converted, I try to get some data contained in the dictionary: in particular I need an array I can access this way:
let myArray = JSONDictionary["data1"][0]["data2"];
XCode really doesn't like this idea, it puts an arrow under the first bracket and says Value of optional type "AnyObject?" not unwrapped, did you mean to use "!" or "?" ?. I follow its suggestion and I insert a "!", converting my preceding code to this:
let myArray = JSONDictionary["data1"]![0]["data2"];
At this point, the following line (where I count the number of elements in data2) shows an error, stating AnyObject has no member count.
The only thing that seems to work fine is this solution but, apart from being ugly and unreadable, I really don't understand it:
let myArray = (JSONDictionary["data1"]?[0]["data2"])!;
Can you help me understand why this basic access to a key in a dictionary must be so intricate?
I must say I like Swift but I spend a lot of time dealing with optionals and bizarre XCode alerts.
There is no guarantee that your JSON dictionary will contain a value for the key data1 (OK, you know it will, but Swift doesn't) so JSONDictionary["data1"] returns an optional. You need to unwrap the optional with ? or !
Also, since you have an NSDictionary, not a Swift dictionary, Swift doesn't know the type of the values, so they are AnyObject. Now again, you know it is an array, but Swift doesn't so you get an error stating that AnyObject doesn't have a count method.
While it is more verbose, it is cleaer for both the compiler and anyone else looking at your code if you split the line into multiple lines. It also lets you downcast the various objects so that Swift knows what is going on and handle any malformed JSON;
if let array1 = JSONDictionary["data1"] as? NSArray {
if let dictionary1 = array1[0] as? NSDictionary {
if let data2Array = dictionary1["data2"] as? NSArray {
let count=data2Array.count
}
}
}
You could implement appropriate else statements to handle errors
Optionals are one of Swift's most powerful features. They help avoid a whole family of bugs associated with uninitialised variables and special sentinnel values for boundary conditions. It is important that you learn how they can help you and not just throw ? or ! at your code until it compiles.
I'm trying to convert this piece of ojb-c code:
MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue];
to swift, and as such I have this:
var finishReason: MPMovieFinishReason = notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey.integerValue];
However this gives me the error in the title.
What am I doing wrong here?
You have a few problems, first of all in your direct translation:
var finishReason: MPMovieFinishReason = notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey.integerValue];
The ] is in the wrong place, so you're trying to call integerValue on MPMoviePlayer... instead of on the dictionary lookup.
var finishReason: MPMovieFinishReason = notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey].integerValue;
This is still failing because userInfo is an NSDictionary and not a Dictionary, so it gets mapped to [AnyObject:AnyObject] It seems like the automatic morphing is failing you, so it's falling back to a CFString which isn't mappable to AnyObject.
The process becomes a little clearer if we break it up more:
// Recover the original dictionary, and cast is by input and output, to it
// all makes sense. This will allow String as a parameter and return an NSNumber
// as a result.
let userInfo = notification.userInfo as [String:NSNumber]
// From the userInfo, let's extract the raw NSNumber using lookup
let reason = userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]
// Convert the NSNumber into an MPMovieFinishReason enum. Note that it's
// 0215 and I'm on ambien so I don't care about the force unwrapping that
// should be optional unwrapping
let finishReason = MPMovieFinishReason.fromRaw(reason!.integerValue)
Many problems can be broken down into much simpler problems if you just take it a single step at a time. Modern compilers aren't even too bothered by it as the can recognize when variables are no longer in use and do proper reclamation. Also note using let instead of var for those temporary values since they shouldn't be reused anyway.
Try the type-casting function Int()
ie.,
var finishReason = Int(notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey]) as MPMovieFinishReason;