CLLocation2D doesn't get value assigned from a variable - ios

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

Related

strange optional behaviour in Swift

I have created my own class in Swift as below.
class Product: NSObject {
var product_id:Int?
var product_number:String?
var product_price:Float?
var product_descrption:String?
}
Now i am setting value in each property like this
let p=Product()
p.product_id=1
p.product_price=220.22
p.productdescrption="Some description"
p.product_number="W2_23_233"
But when i get the value from price then for price i get value like "Optional 220.22" But i don't get appended word "Optional" in description".So to resolve this i added "!" for unwrapping the value of float but i did not have to do this for String please tell why this is happening?
If you are printing any of these values should say Optional(...). If you are assigning the values to a label, that will not include the Optional(...), The reason that it shows Optional(...) when you print the value using print(), is just to show you its an optional. For safety, instead of using the !, try using if lets.
An example with your code,
if let id = p.product_id {
print(id) //Does not contain Optional()
}
You can also combine them, to do them all at one time. (Only do this if you don't want to print unless all values are non-nil)
if let id = p.product_id,
let price = p.product_price,
let description = p.productdescrption,
let productNumber = p.product_number {
//Enter code here that does something with these values
}
Note, if you aren't on swift 3, I believe you only have to write let on the first condition.
If you print any optional variable without unwrapping no matter what type it is, Optional will be appended to the variable's value.
print(p.product_price) will print Optional(220.220001)
print(p.product_descrption) will print Optional("Some description")
To print only value you need to unwrap the optional variables.
print(p.product_price!) will print 220.22
print(p.product_descrption!) will print Some description
This forced unwrapping will only work if the optionals does not contain nil. Otherwise it will give you a runtime error.
So to check for nil you can use if let statement.
No matter what type of variable. If you assign a value to an optional variable, It always enclosed with Optional(...)
Optional without forced unwrapping:
print("product_price = \(p.product_price) \n product_descrption = \(p.product_descrption)")
Output:
product_price = Optional(220.22)
product_descrption = Optional(Some description)
Optional with forced unwrapping:
print("product_price = \(p.product_price!) \n product_descrption = \(p.product_descrption!)")
Output:
product_price = 220.22
product_descrption = Some description

Swift NSUserDefaults app crash first time

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.

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.

Optional issue on Swift

I am newbie on Swift.
I am trying to learn basics and structure. I have started a project, i am learning as i advance.
fatal error: unexpectedly found nil while unwrapping an Optional value
I am taking the exception above a few times while coding.
Although I have read the Apple documentation, my mind is not clear.
Could anyone tell the story about this exception and its causes, how is related with Optional on Swift?
Thanks
Code is as below
var globalDaily:Float = 0.0
for var i=0; i<favoritesArray.count; ++i {
var tempDict:NSMutableDictionary = favoritesArray.objectAtIndex(i) as NSMutableDictionary
let tempFloat:Float! = tempDict.objectForKey("amount") as? Float
globalDaily = globalDaily + tempFloat//This line gives the bad access
}
In swift a variable must always contain a valid value. For value types (int, float, strings, structs, etc.) it means the variable must be initialized. For reference types (instance of classes) they must be initialized to a valid instance of a class and cannot be nil.
In swift a variable cannot be left uninitialized. But there are cases when it is allowed for a variable to be non initialized or initialized with nil. This is why the concept of optionals has been introduced. An optional variable can contain a valid value for its data type, or nil. An optional variable is declared by postfixing the question mark to the type, for instance: var x = Int?.
Suggested reading: Optionals in the Swift Programming Language book.
As for your problem, here:
let tempFloat:Float! = tempDict.objectForKey("amount") as? Float
you read a value from a dictionary, which can be nil if no value has been set for the amount key. That's why there is a cast as? Float. That casts to an optional type, which can either contain a valid Float type, or nil.
In the left side of the assignment let tempFloat:Float! you are stating that the right side is not nil (by using the exclamation mark), and that you can use tempFloat without unwrapping it.
If the dictionary contains a valid float for the amount key, then that's not a problem. But if the dictionary doesn't contain a value, what happens is that a nil is attempted to be converted to a Float when you try to use the tempFloat variable - which causes the exception.
The workaround looks like this:
let tempFloat = tempDict.objectForKey("amount") as? Float
if let unwrappedFloat = tempFloat {
globalDaily = globalDaily + unwrappedFloat
}
this makes sure that you use the variable (and do the addition) only if tempFloat contains a valid float value.
I think you can use downcasting to Float only if Dictionary has AnyObject type
See example:
var globalDaily:Float = 0.0
var favoritesArray:Array<Dictionary<String,AnyObject>> = []
var item:Dictionary<String,AnyObject> = ["amount": 2.0]
favoritesArray.append(item)
for var i=0; i<favoritesArray.count; ++i{
var tempDict:Dictionary = favoritesArray[i]
if let tempFloat:Float = tempDict["amount"] as AnyObject! as Float!{
globalDaily = globalDaily + tempFloat // output 2
}
else{
globalDaily = globalDaily + 5
}
But if the key doesn't exist, we get 5
So if you know that you have Float type only, you can write just:
var globalDaily:Float = 0.0
var favoritesArray:Array<Dictionary<String,Float>> = []
var item:Dictionary<String,Float> = ["amount": 2.0]
favoritesArray.append(item)
for var i=0; i<favoritesArray.count; ++i{
var tempDict:Dictionary = favoritesArray[i]
if let tempFloat:Float = tempDict["amount"]{
globalDaily = globalDaily + tempFloat
}
else{
globalDaily = globalDaily + 5
}

Resources