App crashed when getting string from Dictionary (swift) - ios

I converted a JSON to Dictionary and got some String by
title = json?.objectForKey("Titel_Live") as! String
But some times app will be crashed. I cannot reproduce this problem, just get information from crash reports.
Could someone help me and tell why? Thanks
Error at line 163
Crash reports

title = json?.objectForKey(“Titel_live”) as! String
This line of code where you are doing force unwrapped (Don't force the cast using !) is the cause means if object with key Titel_live dot not find then should be crashed, better go with optional chaining or use gaurd but yes your Json does't contain any object with key Titel_live(may be spelling mistake or object is array so validate once).
//better go like this check if exist or not.
if let t = json?.objectForKey(“Titel_live”) {
title = t
}

You should not force the casting to String.
You can try :-
title = json?.objectForKey("Title_Live") as? String (if title is optional variable)
if title is not optional then use:
title = (json?.objectForKey("Title_Live") as? String ?? "")
Because objectForKey will return nil if no value is associated with that key and force casting nil to String fails and causes crash.

Related

How can I convert a string in a textfield to an Int in Swift?

I tried for a long time to turn the text into an Int but it did not work. I tried it like this:
(AnzahlString is a textfield)
var AnzahlAInt = 0
if let AnzahlAString = AnzahlString.text {
let AnzahlAInt = Int(AnzahlAString)
}
But then I always get the error:
Value of optional type 'Int?' must be unwrapped to a value of type 'Int'
Then I added a ! at the end of Int(AnzahlAString)! so I don't get a error, but now when I press on the button, the app crashes. It was predictable, but how can I change this now to an Int without the !?
At first glance, it looks like you have two things to check for:
is AnzahlString.text present, and
does it represent an Int
The first check is in fact not necessary, since .text will never return nil, even though it's marked as Optional. This means you can safely force-unwrap it.
The second check is easily done by using the ?? operator:
let AnzahlAInt = Int(AnzahlString.text!) ?? 0
PS, just as a stylistic hint: variable names in Swift ususally start with a lowercase letter, names starting with capital letters are used for types.
PPS: your code as written shadows AnzahlAInt - the value of your var is never changed.
The reason why the resulting Int is optional, is that parsing might or might not succeed. For example, if you try to parse the string "Fluffy Bunnies" into an Int, there is no reasonable Int that can be returned, therefore the result of parsing that string will be nil.
Furthermore, if you force the parser by using !, you're telling Swift that you know for sure that the string you pass will always result in a valid Int, and when it doesn't, the app crashes.
You need to handle the situation in which the parse result is nil. For example:
if let AnzahlAIntResult = Int(AnzahlAString) {
// We only get here if the parse was successful and we have an Int.
// AnzahlAIntResult is now an Int, so it can be assigned to AnzahlAInt.
AnzahlAInt = AnzahlAIntResult
}
You did a good job so far but missed out one thing.
This line tries to convert the String into an Int. However this can fail, since your String can be something like this "dfhuse".
let AnzahlAInt = Int(AnzahlAString)
This is why the result of Int(AnzahlAString) is an Optional (Int?). To use it as an real Int, you have to unwrap it.
First solution is the !, however, every time this does fail your app crashes. Not a good Idea to use so.
The best solution would be Optional Binding, as you already used to get the text of your text field.
if let AnzahlAString = AnzahlString.text {
if let safeInt = Int(AnzahlAString) {
// You can use safeInt as a real Int
} else {
print("Converting your String to an Int failed badly!")
}
}
Hope this helps you. Feel free to ask again if something is unclear.
For unwrapping you can also use guard like this
Simple and easy
guard let AnzahlAInt = Int(AnzahlString.text!) else {
return
}
print(AnzahlAInt)

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.

how to take string from returned plist array

When I am trying to take the value from a plist and append it to an array
nameArray.append(namesArray!.objectForKey("Item1")! as! String)
The target item is a string but it appears to be inside the plist array, can anyone explain how to get it out please?
The print of namesArray!.objectForKey("Item1")! followed by the error are shown below:
Cast the value of the "Item1" key as an array of Strings, then fetch the first object from the array (since it appears there's only one). And if you like the idea that your app should not crash everytime a value is nil, better use if let than force-unwrapping everything with !.
Example:
if let names = namesArray,
let items = names.objectForKey("Item1") as? [String],
let result = items.first {
nameArray.append(result)
}
Just take the first element of your array:
nameArray.append((namesArray!.objectForKey("Item1")! as! [String])[0])

NSUserDefaults properly storing Strings in SpriteKit Swift

So I set up a NSUserDefault to store a string in my GameViewController
NSUserDefaults.standardUserDefaults().setObject("_1", forKey: "SkinSuffix")
The idea is it stores a suffix which I will attach to the end of an image name in order to save what skin of a character the player should use.
When I call the value in my GameScene class like so
var SkinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix")
println(SkinSuffix)
it prints "Optional("_1")" instead of just "_1" so when I try to change the name of my image file like so, it doesn't load the image file
hero = SKSpriteNode(texture: heroAtlas.textureNamed("10Xmini_wizard\(SkinSuffix)"))
How do I fix this issue?
You can unwrap the String using the Optional Binding construct. This avoids a crash of the app if the value is nil.
if let skinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix") {
println(skinSuffix)
}
Update: As correctly suggested in the comment below, I am putting the retrieved value in a constant (let). We should always use constants when we don't need to change the value. This way the Swift compiler can make some optimizations and does prevent us from changing that value.
That's because it's implicitly an optional not of type String. You need to case it as such or unwrap the optional in your println statement.
var SkinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix") as! String
Or in your println: println(SkinSuffix!)
As a side note, you should you camelCase for your variable names.
You can use "??" Nil Coalescing Operator to help you dealing with nil and it also
allows you to specify a default string value.
NSUserDefaults().setObject("_1", forKey: "SkinSuffix")
let skinSuffix = NSUserDefaults().stringForKey("SkinSuffix") ?? ""
println(skinSuffix) // "_1"

Unwrapping NSMutableArray from NSUserDefaults

I am trying to unwrap a NSMutableArray from user defaults, but keep getting the error unexpectedly found nil while unwrapping an Optional value. I tried to check for nil after getting the array, but it is happening on the line that is getting the array from the UserDefaults, so I don't know how to fix this error. Can anyone help?
var classURLs: NSMutableArray = NSMutableArray();
let defaults: NSUserDefaults = NSUserDefaults(suiteName: "group.myCompany.Array")!;
classURLs = NSMutableArray(object: defaults.objectForKey("Class URLs")!);
NSUserDefaults.objectForKey returns an optional for a reason – if the key isn’t present, you get nil back. You shouldn’t just force-unwrap it with !
The most common case is this is the first time you’ve tried reading it without having ever written it.
In which case you probably want a default value, perhaps an empty array:
let classURLs = defaults.stringArrayForKey("Class URLs") as? [String] ?? []
(?? substitutes the value on the right if the value on the left is nil)
Note, it’s probably better if you’re writing Swift to go with a Swift array (e.g. [String]) rather than NSMutableArray, unless all you are going to do is pass it straight into a call to Objective-C.
You can also avoid the ! you’re using with the NSUserDefaults init by using optional chaining:
let defaults = NSUserDefaults(suiteName: "group.myCompany.Array")
var classURLs = defaults?.stringArrayForKey("Class URLs") as? [String] ?? []
If the key does not exist, the forced unwrap (!) of the nil object will result in a crash. You need to account for this case.
var classURLs = NSMutableArray(object:
NSUserDefaults.standardUserDefaults().objectForKey("foo") ?? [])
NB: transitioning from Objective-C? You don't need the semicolons! ;-)

Resources