Swift NSUserDefaults app crash first time - ios

my app crash every time i installed it, but only the first time..
this is my code
var State = save.stringForKey("StateSave")
var City = save.stringForKey("CitySave")
var Vehicle = save.stringForKey("ModelNumberSave")
var ExtensionPeriod = save.stringForKey("ExtensionPeriodChoosed")
var Location = "Location"
if ExtensionPeriod == nil {
var name = ""
var FieldChoosed: Void = save.setObject(name, forKey: "ExtensionPeriodChoosed")
save.synchronize()
}
save.synchronize()
var DetailNames = ["\(State!)","\(City!)","\(Location)","\(Vehicle!)","\(ExtensionPeriod!)"]
it crash because it say that ExtensionPeriod is nil. so i think it's because of the NSUserDefaults in the if statement. So my first idea was to put save.synchronize which var save = NSUserDefaults.standardUserDefaults() but it didn't work :\

Your app crashes because in this line:
var DetailNames = ["\(State!)","\(City!)","\(Location)","\(Vehicle!)","\(ExtensionPeriod!)"]
you are using the force unwrap operator. You need to make sure these values aren't nil before unwrapping them.
From The Swift Programming Language: The Basics:
Trying to use ! to access a non-existent optional value triggers a runtime error. Always make sure that an optional contains a non-nil value before using ! to force-unwrap its value.
You can provide default values using the nil coalescing operator:
var State = save.stringForKey("StateSave") ?? "Texas"
var City = save.stringForKey("CitySave") ?? "El Paso"
As a stylistic note, your instance variables should begin with lowercase letters. Only type names should begin with uppercase letters.

Related

How to add optional values to an array in Swift

I'm having issues with appending an optional value to an array in Swift. The view I'm writing is for the creation of a routine for the gym. However my Routine object is not being instantiated as it should be.
I have experience with other programming languages but I am fairly new to Swift, and optionals.
My ViewController contains an optional variable:
var routine: Routine?
Where the Routine class contains:
name: String
exerciseList: [String]()
numOfSets: [Int]()
When I am preparing it to send the newly created routine to my other ViewController, I take the values from user input to edit the fields of the object.
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0") //numOfSetsOne is a text label
routine?.exerciseList.append(selectedExerciseOne!) //Haven't tested to see if this works yet
routine?.numOfSets[0] = numberOne! //This line is not working
routine = Routine(name: name)
To try a little debugging I put print statements on either side of the line like so:
print ("numberOne Value: \(numberOne!)")
routine?.numOfSets[0] = numberOne!
print ("numOfSets[0] Value: \(routine?.numOfSets[0])")
I expected the output from the second print statement to be identical to the first. However the terminal output:
numberOne Value: 3
numOfSets[0] Value: nil
Does anyone know what has gone wrong here?
Thanks
You have declared a property that may contain a Routine, but you have not assigned an instance of Routine to that property before trying to use it.
This means that, for example,
routine?.numSets[0] = numberOne!
doesn't do anything - routine is nil and so the statement is skipped.
You should create an appropriate init function for your Routine class and use that to create a new Routine and assign it to routine
For example:
class Routine {
var name: String
var exerciseList = [String]()
var numberOfSets = [Int]()
init(named: String) {
self.name = named
}
}
Then you can say
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0")
self.routine = Routine(named: name)
self.routine?.numberOfSets.append(numberOne!)
Coordinating related arrays can get a bit messy, so I would use a single array:
struct ExerciseSet {
let exerciseName: String
let sets: Int
}
class Routine {
var name: String
var exerciseList = [ExerciseSet]()
init(named: String) {
self.name = named
}
}
Your Routine is not initialised before its being assigned value
try
let name = routineName.text ?? ""
let numberOne = Int(numOfSetsOne.text ?? "0")
routine = Routine(name: name)
routine?.exerciseList.append(selectedExerciseOne!)
routine?.numOfSets[0] = numberOne!

CLLocation2D doesn't get value assigned from a variable

I'm getting values for latitude and longitude from Firebase and store as String into aLatitudeArray and aLongitudeArray. That part works well, arrays are populated as the childs change in Firebase. I want then reconstruct an array of CLLocation2D from the earlier arrays, but when I assign the values to a variable it get nil. My function is :
func drawAlerts() { // to rewrite based on aLatituteArray and aLongitudeArray generated from firebase incoming data
var alertDrawArrayPosition = 0
while alertDrawArrayPosition != (alertNotificationArray.count - 1) {
var firebaseAlertLatidute = aLatitudeArray[alertDrawArrayPosition] // get String from alertLaitudeArray
let stringedLatitude: Double = (firebaseAlertLatidute as NSString).doubleValue // converts it to Double
var firebaseAlertLongitude = aLongitudeArray[alertDrawArrayPosition] // get string from alertLongitudeAray
let stringeLongitude: Double = (firebaseAlertLongitude as NSString).doubleValue //converts it to Double
var recombinedCoordinate: CLLocationCoordinate2D!
//
recombinedCoordinate.latitude = stringedLatitude // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
recombinedCoordinate.longitude = stringeLongitude // Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
// alertNotificationArray.append(recombinedCoordinate!) // Build alertNotificationArray
alertDrawArrayPosition = ( alertDrawArrayPosition + 1 )
}
}
I read many posts but no solution suggested worked.
At run values are :
firebaseAlertLatidute String "37.33233141"
stringedLatitude Double 37.332331410000002 ( extra 0000002 added after conversion )
firebaseAlertLongitude String "-122.0312186"
stringeLongitude Double -122.0312186
recombinedCoordinate CLLocationCoordinate2D? nil none ( this is from the error line ).
And from console I get this prints:
fir aLongitudeArray ["-122.0312186"]
fir aLatitudeArray ["37.33233141"]
Why is not assigning the value?
Well, there is no big problem here. You just did wrong using the ! when declaring the recombinedCoordinate variable.
This line declares a variable, and tells Swift: Hey, currently I'm not initializing this, but I'm going to initialize it, believe me.
var recombinedCoordinate: CLLocationCoordinate2D!
But then, on the next line, you are trying to set a variable of this instance.
recombinedCoordinate.latitude = stringedLatitude
See where I am going with this? You have not initialized a CLLocationCoordinate2D instance. recombinedCoordinate is nil. Avoiding nil access is the main reason as to why Swift has the Optional type everywhere.
If you had written CLLocationCoordinate2D? XCode would have told you later, that this call is unsafe, or, it would have not attempted to set the property after seeing that it is nil.
To solve your problem, I'd just write the following:
let recombinedCoordinate: CLLocationCoordinate2D(latitude: stringedLatitude, longitude: stringeLongitude)
Also, I would advise you to improve your variable naming. "stringedLatitude" and "stringeLongitude" make no sense, because they actually are of the Double type.
Finally, I'd avoid using .doubleValue, see https://stackoverflow.com/a/32850058/3991578
You need to init it like this
let recombinedCoordinate = CLLocationCoordinate2D(latitude:stringedLatitude, longitude:stringeLongitude)
As this
var recombinedCoordinate: CLLocationCoordinate2D!
recombinedCoordinate.latitude = stringedLatitude // here recombinedCoordinate is nil as you never initiated it

Reading from a dictionary in Swift - Not working

I am building a game in Xcode and I'm storing the details for the level in text files e.g. Level1.txt, Level2.txt etc.
I read in the data from a text file and store it in a Dictionary.
When I try to assign the values from the dictionary to the global variables, it doesn't work.
Text File Contents (Level1.txt)
LevelNum:1
weaponPickupRate:10.0
weaponPickupAmount:50.0
monsterMinSpeed:10.0
monsterMaxSpeed:15.0
monsterRate:1.0
totalMonsters:10.0
goldPerMonster:10
Global Variables
var settings = [String: Any]()
var monsterMaxSpeed = 0.0
Function For Obtaining Level Details
func GenerateLevel(levelNumber: Int) {
fileName = "level\(levelNumber).txt"
levelPath = "\(NSBundle.mainBundle().resourcePath!)/\(fileName)"
var err: NSError? = NSError()
let s = String(contentsOfFile: levelPath, encoding: NSUTF8StringEncoding, error: &err)
if let content = s {
var array = content.componentsSeparatedByString("\n")
for a in array {
var v = a.componentsSeparatedByString(":")
settings[v[0]] = v[1]
}
}
println(settings) // A
var e = settings["monsterMaxSpeed"]
println(e) // B
monsterMaxSpeed = settings["monsterMaxSpeed"] // C
}
Println(setting) (A) - prints:
[monsterRate: 1.0, monsterMinSpeed: 10.0, weaponPickupRate: 10.0, weaponPickupAmount: 50.0, goldPerMonster: 10, totalMonsters: 10.0, LevelNum: 1, monsterMaxSpeed: 15.0]
Println(e) (B) prints:
Optional("15.0")
This Line Does not work
it shows up an error and doesn't allow me to build my project. The Error given is:
'(String, Any)' is not convertible to 'Double'
monsterMaxSpeed = settings["monsterMaxSpeed"]
Please can someone help and advise me what I need to do?
Thanks,
Ryann
You have two problems here.
First, fetching from a [String:Any] dictionary by key does not return an Any. It returns an Any? i.e. an optional that may or may not contain an Any. This is because that key may not be present in the dictionary.
You need to test if the value is non-nil and unwrap the value if it is:
if let speed = settings["monsterMaxSpeed"] {
monsterMaxSpeed = speed
}
else {
// handle there being no speed setting in your file
// by reporting an error or similar
}
Or, if you’re happy with just using a default, you can use the nil coalescing operator:
// if the key is present, us the unwrapped value, if not use 0.0
monsterMaxSpeed = settings["monsterMaxSpeed"] ?? 0.0
Second, you’ve declared monsterMaxSpeed as a Double not an Any. So once you resolve your optional unwrapping problem you’ll get a second problem. You need to convert the Any to a Double using as?. The ? in as? is important – if the value is not a double (suppose there was a rogue character in the entry in your file), you will get a nil back. Again, you would need to test for this and handle the error.
Happily, you can do this all in one go:
monsterMaxSpeed = (settings["monsterMaxSpeed"] as? Double) ?? 0.0
(it’s probably the confluence of the two of these issues that’s causing you to get a particularly enigmatic error – the error relates to the other version of Dictionary.subscript which takes an index, not a key, and returns a key/value pair, which isn’t optional, because indices should only address entries that are definitely in the dictionary)

How to use (?) and (!) in Swift

I'm new to swift and I'm having some difficulties understanding on how to use (!) and (?)
As far as I know, we can use (?) when there are instances that a variable can be nil.
And use (!) when you are 100% sure that item is not nil.
1. Working Fine - Optionals
var john:String?
john = "Is my name"
println(john!)
2. Crashes on Runtime - ! must not be nil - means this is correct
var john:String?
println(john!)
3. Works Fine
var dict: [String:AnyObject] = Dictionary()
dict["name"] = "John"
var str: String = dict["name"]! as String <--- Taking away the (!) says it's not convertible to String
4. Cannot Run/Build - for me it's similar to 1.
var dict: [String:AnyObject]? = Dictionary() ---error says does not have a member named 'subscript'
dict["name"] = "John"
var str: String = dict["name"]! as String
5. Unexpectedly found nil while unwrapping an optional value
var dict: [String:AnyObject] = Dictionary()
dict["name"]? = "John"
var str: String = dict["name"]! as String
Would be great if someone can help me understand these things. Thanks!
it is a bit misleading interpretation believing when an ! 'marks' an ivar then that 100% cannot be nil. it can be. you can say only, you got the value as already unwrapped, so you don't need to force unwrapping it again – but it can be nil.
try this example for instance:
var text: String! = "hello"
text = nil;
println(text)
it prints a nil for you.
the reason why your app can crash is you force unwrapping an optional which is nil, that is invalid operand.
#4
line-by-line:
var dict: [String:AnyObject]? = Dictionary() // from OP
your dict is an optional, let us see what you are doing here:
dict["name"] = "John" // from OP
var str: String = dict["name"]! as String // from OP
you have an optional dict and you'd like to use it somehow, you have two possible ways to do it:
(A) via optional chaining;
(B) via forced unwrapping;
(A)
dict?["name"] = "John" // optional chaining
it is quite straightforward, it assigns the new value for the key name if the dictionary is not nil, otherwise the chain generously falls and nothing happens in runtime.
in perspective of this line:
var str: String = dict!["name"]! as String // forcibly unwrapped
it crashes in runtime if either the dictionary or the value for the key was nil (as per the first paragraph says: invalid operand to force unwrapping a nil), but the str would be John if the dictionary and the key both do valid objects.
(B)
dict!["name"] = "John" // forcibly unwrapped
it works like a charm and assigns the new value for the key name if the dict exists; but if the dict was nil, that is a termination point in runtime (aka crash), because nil cannot be unwrapped forcibly (see above).
#5
line-by-line:
var dict: [String:AnyObject] = Dictionary() // from OP
your dict is not optional and not even nil, but the dictionary is literally empty, so no key does exist in it, including the name.
dict["name"]? = "John" // from OP
var str: String = dict["name"]! as String // from OP
the optional chaining always falls when any of the element of the chain falls – therefore no new value will be assigned in your code, but the falling happens gracefully, so you bypass the first line about assigning the new value, but the app crashes in the second line because the value does not exists and you try to force unwrapping it (see above about invalid operand).
so, you need to drop the optional chaining from the first line, if you want to assign a new value for a non-existing key:
dict["name"] = "John"
the optional chaining is useful if you would not like to change the original dictionary with adding a new key/value, but you would like to override an existing one only:
dict["name"] = "John"
dict["name"]? = "Jack"
in that case the new value will be Jack, because the optional chaining won't fall as the key name is already existing with a different value, so it can be and will be overridden; but:
dict["name"] = nil
dict["name"]? = "Jack"
the optional chaining will falls and no new value is assigned here for the key.
NOTE: there would be many other things and ideas which can be told about the concept. the original documentation is available on Apple site under section Swift Resources.

Difference between optional and normal String

Difference between String?,String! and String
I am using this code :
var myString:String? = nil
var myString1:String = ""
var myString2:String! = nil
println(myString2)
Here it's giving nil in myString2 instead of run-time error.
There are better explanations out there, but the simple version is:
String is a String. It holds a String value.
String? is an Optional String. It can hold a String value or nil. You can unwrap the optional and immediately try to access the String value by using ! like this:
var str: String? = nil
str!.length
That code will result in a runtime error because str is nil. A safer way to get the value of the optional is to use an if let:
var str: String? = nil
if let myStr = str{
myStr.length
}
That has the same functionality as the above code, but won't crash on nil values.
String! is an implicitly unwrapped optional. It works that same way as a regular optional but it is assumed to be non-nil, so when you call it it tries to access the value like it was a regular optional with ! after.
The reason println works on all these types is because it can take Strings, String Optionals, and nil values and handles them all.
var str: String? = "hello world"
println(str)
println(str!)
println(nil)
All should work.
#connor's answer is correct.
var Amy:Wife? // Amy is your girlfriend, she may or may not be your wife.
var Amy:Wife // Amy is your wife.
var Amy:Wife! // Amy will be your wife, no matter you like it or not.

Resources