Swift equivalent to `initWithContentsOfFile:`? - ios

I am trying to use the Swift equivalent of initWithContentsOfFile: on an Array. The documentation states that the equivalent is convenience init(contentsOfFile aPath: String!)
I have tried to call this with the following code:
var path = NSBundle.mainBundle().pathForResource("Standards", ofType: "plist")
var rawStandards = Array<Dictionary<String, AnyObject?>>(contentsOfFile: path)
The compiler is telling me, however, that it couldn't find an overload that accepts the supplied arguments. What am I missing?

#Leo Natan was really close, but the way that worked for me was:
var rawStandards = NSArray(contentsOfFile: path)
I was then able to access the elements:
for obj: AnyObject in rawStandards {
if var rawStandard = obj as? NSDictionary {
}
}

You are initializing a Swift array, not an NSArray, which has the initWithContentsOfFile: initializer method.
Try:
var rawStandards = NSArray(contentsOfFile: path) as Dictionary<String, AnyObject?>[]

var path = NSBundle.mainBundle().pathForResource("Standards", ofType: "plist")
var menuItemArray = NSArray(contentsOfFile: path) as NSArray!
this is what i'm using myself. in viewDidLoad

Here is what worked for me. I've verified it in the beta build of Xcode. Unlike the other answers, I wanted a native typed Swift array with the results, so I took a few extra steps. (#wayne-hartman's answer may be more intuitive if you don't know what you have in your plist or don't want a typed Array.) It did require forcing types in a way I didn't expect.
My plist has dictionaries at the first level and strings at the second level, the code should be easy to adjust for other types.
let plistNSArray = NSBundle.mainBundle().pathForResource("myplist", ofType: "plist")
let untypedArray: AnyObject[] = plistNSArray
let typedArray = untypedArray as Dictionary<String,String>[] //Obviously you could do this conditionally for better error handling
for plistDictItem in typedArray {
for (dictKey,dictValue) in plistDictItem {
println("key \(dictKey) has value \(dictValue)")
}
}
It doesn't work (or at least I couldn't make it work) without the intermediate step of untypedArray.
#leo-natan's doesn't compile for me, even though it looks like it should to me. I believe that the compiler just can't automatically convert the NSArray into a typed Swift array directly without the intermediate step I added. It wouldn't surprise me if his code works in a future release.

Related

Ambiguous use of 'mutableCopy()' Swift3

I tried to update Swift 3 and I got the following error :
Ambiguous use of 'mutableCopy()'
Before update to swift 3. It runs well.
Swift 2.3
NSUserDefaults.standardUserDefaults().objectForKey("listsavednews")?.mutableCopy() as! NSMutableArray
Swift 3.0
(UserDefaults.standard.object(forKey: "listsavednews")? as AnyObject).mutableCopy() as! NSMutableArray
I found that mutableCopy in Swift3 return Any that doesnt have method mutableCopy() so that it needs to cast to AnyObject.
Any helps thanks.
I dont know why I can't comment.
Thanks all, I'll be using :
UserDefaults.standard.mutableArrayValue(forKey: "listsavednews")
mutableCopy is an Objective-C method from NSObject. There's little reason to use it in Swift 3.
Since you are dealing with UserDefaults and mutableCopy, you must be dealing with either an array or dictionary. Or it could be a string.
The proper way to do this in Swift 3 is to use the proper UserDefaults method to get an array or dictionary. And assign the result to a var. That combination will give you a mutable array or mutable dictionary.
var someArray = UserDefaults.standard.array(forKey: "somekey")
or:
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey")
In the two above cases, you end up with an optional since there might not be any data for the given key. And you also get a non-specific array or dictionary which isn't ideal. It would be better to cast the result to the appropriate type.
Let's say you have an array of strings and you want an empty array if there is nothing currently in user defaults. You can then do:
var someArray = UserDefaults.standard.array(forKey: "somekey" as? [String]) ?? []
Adjust as necessary if the array contains something other than String.
If you actually have a dictionary, the code would be similar.
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey") as? [String:String] ?? [:]
If your original object is just a string, then you could do:
var someString = UserDefaults.standard.string(forKey: "somekey") ?? ""

Intricate access to dictionary key

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.

Cannot subscript a value of type anyobject

I've currently upgraded to the new Xcode 7, and the following code never had any errors with swift 1.2, but now its telling me that :
Cannot subscript a value of type any object
var imageArray : NSArray = []
let url = NSURL(string: (self.imageArray[indexPath.row][0] as? String)!)
I know its about [0] but how do i rewrite it to be accepted ?
OK, so first you are using an NSArray. You can drop that and make everything much easier.
In Swift always use strong typing where possible. Avoid Any and AnyObject when they are not needed. (They are VERY RARELY needed).
The error is happening because you're not telling the code what is actually in the imageArray.
Also, imageArray tells me the array is full of images. Name your variables more descriptively. imageUrlStringArray or arrayOfImageUrlArrays. Something more descriptive anyway.
Declare imageArray like...
var imageArray = [[String]]()
This tells the compiler that imageArray is a 2D array with Strings at the second level.
Now you can create your URL easily...
guard
let urlString = imageArray[indexPath.row].first,
let url = NSURL(string: urlString)
else { // Handle the inability to create a URL }
// Do something with the url

Converting a Swift UnsafePointer<AudioStreamBasicDescription> to a Dictionary?

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)

Unable to use AnyObject in Swift

I don't understand an inch of how the AnyObject type in Swift is supposed to work. I am loading a Plist file into a NSDictionary instance in my Swift code (not shown here). Later I try to use some of the values from the Plist file in a UIPickerView:
// Trying to extract the first fruit
let fruitKeys = fruits.allKeys
let fruitKey = fruitKeys[0]
// This is another NSDictionary
let fruit = fruits.objectForKey(fruitKey)
// Getting a property of a fruit
let nutritions = fruit.objectForKey("nutritions")
let nutritionKeys = nutritions.allKeys
However I am not able to get the keys of a fruit by calling nutritions.allKeys. I get Could not find member allKeys. However, which part of the documentation have I misunderstood? I read that you can call any objective c method on an instance of AnyObject. Why do I need to cast or to some other magic here?
Should I cast everything to their real types? Or should I use AnyObject??
In short, yes you would generally want to cast to your defined type, here's how I would write it:
let fruitKeys = fruits.allKeys as [String]
let fruitKey = fruitKeys[0]
let fruit = fruits[fruitKey] as NSDictionary
let nutritions = fruit["nutritions"] as NSDictionary
let nutritionKeys = nutritions.allKeys
As I already mentioned in my comment, a possible change to your code would be:
let fruitKeys = fruits.allKeys
let fruitKey = fruitKeys[0]
let fruit = fruits.objectForKey(fruitKey)
let nutritions = fruit.objectForKey("nutritions")
let nutritionKeys : AnyObject? = nutritions.allKeys
I omitted downcasting every value because I saw it as pointless, so you might encounter some warnings, but it will work.

Resources