I have a plist file where I added level info. It is setup as follows: It has a dictionary called patterns, then it has an array called 1, and that array contains n items, which are all dictionaries. These items have the 3 booleans and three numbers. This is my code for reading that info:
func readPlstForBlocks(){
let levelPlist = NSBundle.mainBundle().pathForResource("level\(levelToShow)", ofType: "plist")
let levelData = NSDictionary(contentsOfFile: levelPlist!) as NSDictionary!
let patterns = levelData["patterns"] as! NSDictionary
let firstPat = patterns["1"] as! NSArray
for item in firstPat {
let i = item["x"]?!.floatValue
let j = item["y"]?!.floatValue
let movingUpwards = item["movingUpwards"] as! Bool
let movingSidewards = item["movingSidewards"] as! Bool
let spinning = item["spinning"] as! Bool
let typ = item["type"]?!.floatValue
let type = Int16(typ!)
let posx = CGFloat(i!)
let posy = CGFloat(j!)
}
}
Now the lines let movingUpwards = item["movingUpwards"] as! Bool
let movingSidewards = item["movingSidewards"] as! Bool
let spinning = item["spinning"] as! Bool gives me a strange error, saying that i am casting from SKNode to bool and it always fails. That totally confuses me, since I have been accessing booleans from other plist with similar code plenty of times and it seemed to work just fine, and secondly, why on earth xcode would think its SKNode? Anyone knows how to solve this?
EDIT:
I am adding the plist file photo:
In general, when writing Swift code, I would recommend using Swift types as much as possible, not Objective-C types. So, where you have this:
let levelData = NSDictionary(contentsOfFile: levelPlist!) as NSDictionary!
let patterns = levelData["patterns"] as! NSDictionary
let firstPat = patterns["1"] as! NSArray
I would write this:
let levelData = NSDictionary(contentsOfFile: levelPlist!) as! [NSObject:AnyObject]
let patterns = levelData["patterns"] as! [NSObject:AnyObject]
let firstPat = patterns["1"] as! [[NSObject:AnyObject]]
Now, if we get that far without crashing, Swift knows that firstPat is an array of dictionaries, which is much more than you were telling it before. This means that for item in firstPat can correctly type item as a dictionary. And that, in turn, means that you can subscript it and extract values by key:
for item in firstPat {
let i = item["x"] as! Float
let j = item["y"] as! Float
let movingUpwards = item["movingUpwards"] as! Bool
// ...
}
None of that is "good" code; the forced casts are dangerous. In real life, you should be unwrapping / casting safely the whole way down. But I think this will give you a more coherent approach up front.
Related
Below I try to make an array ChatListings, but it doesn't work.
let chatRef = FIRDatabase.database().reference().child("chatListings")
override func viewDidLoad() {
super.viewDidLoad()
let firstQuery = chatRef.queryOrdered(byChild: "userID").queryEqual(toValue: userID)
firstQuery.observe(FIRDataEventType.value, with: { snapshot in
for child in snapshot.children {
print("child is \(child)")
if let dict = snapshot.value as? Dictionary<String, AnyObject>{
print("dict is \(dict)")
let roomKey = dict["chatRoomKey"] as! String
let oUID = dict["otherUserID"] as! String
let oUserName = dict["otherUserName"] as! String
let oProfilePic = dict["otherUserProfilePic"] as! String
let userIDTemp = dict["userID"] as! String
chatListing = ChatListing(chatRoomKey: roomKey, UID: userIDTemp, name: oUserName, otherUserID: oUID, otherUserProfilePicURL: oProfilePic)
chatListings.append(chatListing)
}
}
print("chatListings = \(chatListings)")
})
}
This crashes saying that the compiler unexpectedly found nil while unwrapping an Optional value. I don't know why it won't work. I've tried every which way I can find to extract the data that the compiler reads moments before crashing or failing to fill an array of my 'chatlisting' objects.
Here's an example of the data that the compiler reads but cannot extract with maybe 4 different coding attempts:
"-KjdSF97Q2z3afXzkwQ9": {
chatRoomKey = "-KjdSF97Q2z3afXzkwQ9";
messages = {
"-KjdSOVTsg8jEy6SeEA2" = {
MediaType = PHOTO;
fileUrl = "https://firebasestorage.googleapis.com/v0/b/preollify.appspot.com/o/mget8KN2nHe4sOhbnWTixYvCOrr2%2F515963239.371526?alt=media&token=6cb12ec1-5bdb-43a1-ab49-90c90570b341";
senderId = mget8KN2nHe4sOhbnWTixYvCOrr2;
senderName = Michael;
};
"-KjdSPxpNT0pkQ1y5-_1" = {
MediaType = VIDEO;
fileUrl = "https://firebasestorage.googleapis.com/v0/b/preollify.appspot.com/o/mget8KN2nHe4sOhbnWTixYvCOrr2%2F515963229.282051?alt=media&token=04671c8e-d7f1-49f2-81d0-09836c034ae2";
senderId = mget8KN2nHe4sOhbnWTixYvCOrr2;
senderName = Michael;
};
"-KjdVaVTfbaC-3S-91-A" = {
MediaType = TEXT;
senderId = mget8KN2nHe4sOhbnWTixYvCOrr2;
senderName = Michael;
text = The;
};
};
otherUserID = aRandomUser3611;
otherUserName = Michael;
otherUserProfilePic = "https://firebasestorage.googleapis.com/v0/b/preollify.appspot.com/o/ProfilePictures%2Fmget8KN2nHe4sOhbnWTixYvCOrr2%2FmediumProfilePicture.jpg?alt=media&token=d88afa5d-0db7-4ce2-95c9-3038ff592e9f";
userID = mget8KN2nHe4sOhbnWTixYvCOrr2;
I'm trying to extract all the data but the messages part, which I plan on doing later in the app.
This data (excluding the "messages" part) gets written in the chatViewController's viewDidLoad like this:
let preMessageRef = chatRef.childByAutoId()
chatListingID = preMessageRef.key
let initialChatRoomData = ["chatRoomKey": chatListingID, "otherUserID": otherUID, "otherUserName": otherUserName, "otherUserProfilePic": otherUserProfilePicURLString, "userID": userID]
preMessageRef.setValue(initialChatRoomData)
Retrieving data from Firebase Database has been completely hit or miss for me, with copying the successful attempts of extracting data rarely working twice. Their documentation is minimal to the point of leaving out way too much as it provides little help for how to extract data in real world contexts. Why do people like Firebase? It has been a very frustrating experience working with Firebase and I definitely regret it. But it's probably too late to turn back and go with something better, i.e. a platform that provides clear instruction for how to get it to work.
I think you just have a silly typo. Try this:
let childData = child as! FIRDataSnapshot
print("child key: \(childData.key)")
if let dict = childData.value as? Dictionary<String, AnyObject> {
...
}
that is, use child instead of snapshot.
Update. Turns out using NSDictionary, rather than Dictionary, fixed the dict constant crashes. But, besides compiler bugs, still not clear why...
I am working on Swift 3.0 in which I have taken an array, Now I want to get the value from a particular index with value for key from a dictionary. I tried multiple ways but still not able to find the solution.
For reference in Objective C we have to use this
[arrGroupList objectAtIndex:0]valueForKey:#""]]
I want its equivalent in Swift 3.0.
Any help will surely be appreciated!!!
let val = arrGroupList[0]
print(val["YYZ"])
You can do this:
// array of [[String:String]] Dict
let myArray = [[String:String]]()
let first = myArray.first
let second = myArray[1]
let last = myArray.last
In your case (assuming that arrGroupList contains [String: String]), it should look like this:
let firstValueInFirstDict = arrGroupList[0]["myKey"]
Note That firstValueInFirstDict is an optional String.
EDIT:
If you are getting Any instances instead of [String: String], you should check it:
guard let firstDict = arrGroupList[0] as? [[String: String]] else {
return
}
let firstValueInFirstDict = firstDict["myKey"]
Hope that helped.
let item=newdata[indexPath.row] as! NSDictionary
//let newitem=String(item["name"])
cell.textLabel?.text=String(describing: item["name"]!)
cell.detailTextLabel?.text=String(describing: item["city"]!)
I get an error with the following code:
func prepareAnimationForDictionary(settings: NSDictionary,repeated: Bool) -> SKAction {
let atlas: SKTextureAtlas =
SKTextureAtlas(named: settings["AtlasFileName"] as! String)
let textureNames:NSArray = settings["Frames"] as! NSArray
let texturePack: NSMutableArray = []
for texPath in textureNames {
texturePack.addObject(atlas.textureNamed(texPath as! String))
}
let timePerFrame: NSTimeInterval = Double(1.0/(settings["FPS"]
as! Float))
let anim:SKAction = SKAction.animateWithTextures(texturePack,
timePerFrame: timePerFrame) // the error I get is here
if repeated {
return SKAction.repeatActionForever(anim)
}else{
return anim
}
Just use the expected (Swift) types
...
let textureNames = settings["Frames"] as! [String]
var texturePack = [SKTexture]()
for texPath in textureNames {
texturePack.append(atlas.textureNamed(texPath))
}
...
From the Swift point of view the mutable Foundation collection types NSMutableArray and NSMutableDictionary are type unspecified and are not related to the Swift native counterparts.
Change timePerFrame
to (timePerFrame as [AnyObject]) as! [SKTexture]
Ok, think about this. You have a variable texturePack. You don't show the declaration, but based on the error message, I'm going to assume it's of type NSMutableArray. The call in question wants an array of SKTexture objects.
So, cast your texturePack to the required type:
let anim:SKAction = SKAction.animateWithTextures(texturePack as! [SKTexture],
timePerFrame: timePerFrame) //error i get is here
Note that if there is any chance that texturePack is not an array of SKTexture objects, you would be better off using if let or guard to check that the cast succeeds:
guard
let anim:SKAction = SKAction.animateWithTextures(texturePack as! [SKTexture],
timePerFrame: timePerFrame) //error i get is here
else
{
return nil;
}
Or, as the others have suggested, change the declaration of your texturePack array to be of type [SKTexture]
I'm having a lot of frustration with Swift when it comes to working with Dictionaries and NSDictionaries.
I am pulling data from a server. One of the values is a Bool.
I started with a Swift Dictionary and moved to NSDictionary out of frustration. However, I still cannot get the values from the dictionary.
All of the following fail with contradictory errors:
let unread:Bool = data!["unread"] as! Bool
let unread:Bool = data?["unread"] as! Bool
let unread:Bool = data?.objectForKey("unread") as! Bool
let unread:NSNumber = data?["unread"] as! NSNumber
error: Could not cast value of type 'NSTaggedPointerString' (0x110c1eae8) to 'NSNumber' (0x10e0ab2a0).
Okay, looks like that data is coming in as a String... let's try:
let unreadStr:String = data!["unread"] as! String
let unreadStr:NSString = data!["unread"] as! NSString
error: Could not cast value of type '__NSCFBoolean' (0x1097413b8) to 'NSString' (0x106bcdb48).
So I'm confused. When I try to convert it to a Bool it says I cannot convert a String to a Number. When I try to convert it to a String, it says I cannot convert a number to a string.
Here is what the data looks like:
Optional({
senderId = 10154040;
sent = 1460844986973;
text = "Test Message 1";
unread = false;
})
You should try something along these lines:
let data: [String : Any] = ["first" : "test", "second" : 2, "third" : true]
let first = data["first"] as! String
let second = data["second"] as! Int
let third = data["third"] as! Bool
print(first)
print(second)
print(third)
Why do I get an error when I used valueForKey... I am using same trick like in objectiveC ...
In ObjectiveC, the code is
self.strSubscribe =[responseObject[#"subscribe"] valueForKey:#"subscribe_ids"];
In Swift , the code is
self.strSubscribe = responseObject["subscribe"].valueForKey["subscribe_ids"] as! String
I declare the variables like
var arraySubCategory : NSMutableArray! = NSMutableArray()
var strSubscribe:String!
And I tried to access the value from below response
{
subscribe =
{
"subscribe_ids" = "1,14";
}
}
Edit
It works using Amit and Eric's solution but now for following data
{
data = (
{
"subscribe_ids" = "1,14";
}
);
}
let dictionary = responseObject["data"][0] as! Dictionary<String,AnyObject>
self.strSubscribe = dictionary["subscribe_ids"] as! String
OR//
if let dic = responseObject["data"][0] as? [String:String], let ids = dic["subscribe_ids"] {
self.strSubscribe = ids
}
but it gives me error:
could not find member 'subscript'
Swift doesn't know the type of responseObject["subscribe"], you have to help the compiler a bit; for example:
if let dic = responseObject["subscribe"] as? [String:String], let ids = dic["subscribe_ids"] {
self.strSubscribe = ids // "1,14"
}
UPDATE:
It's still the same problem: the compiler doesn't know the type of responseObject["data"], so when you try to access the subscript there's an error (because you know it's a dictionary inside the array, but the compiler doesn't).
One solution is to give the type to the compiler by declaring an array of dictionaries in the if let condition:
if let arr = responseObject["data"] as? [[String:String]], let ids = arr[0]["subscribe_ids"] {
self.strSubscribe = ids
}
Notice that it's [[String:String]] (array of dictionaries), not [String:String] (dictionary).
Write like this.
let dictionary = responseObject["subscribe"] as! Dictionary<String, AnyObject>
self.strSubscribe = dictionary["subscribe_ids"] as! String
Since responseObject["subscribe"] will give a AnyObject? output and AnyObject does not have any member called valueForKey.