Optional issue on Swift - ios

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
}

Related

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

Cannot assign value of type 'String?' to type 'Int'

I am getting the error message Cannot assign value of type 'String?' to type 'Int'
I have browsed through other questions like this but it still shows the error.
if sunscreenName.text != nil && reapplyTime.text != nil {
sunscreen = sunscreenName.text!
reApplyTime = reapplyTime.text
//Some sort of message such as Progress hud
}
Thanks in advance!
I got your problem, actually what happens here Swift is is type safe langauge
So what you are doing is is to store a String value in Int which will not happen automatically you need to convert it to Int
like this
Int(sunscreenName.text)
But there is a catch there not all string are convertible to Int type, fo e.g.
let name = "roshan"
if you try to convert it to Int it will give you a nil
let a = Int(name)
So its better you do a optional Binding here provided by Swift
if let sunValue = Int(sunscreenName.text),let reApplyValue = Int(reapplyTime.text) {
sunscreen = sunValue
reApplyTime = reApplyValue
}
I recommend reading through The Swift Programming Language to get a better understanding of Swift and its fundamental concepts, since this question is fairly basic.
You make several mistakes:
if sunscreenName.text != nil && reapplyTime.text != nil {
This is wrong. In Swift, if you plan to use the value later, you should use if let rather than comparing to nil. Comparing to nil leaves the values optional, but if let unwraps them. So, do this instead:
if let sunscreenText = sunscreenName.text, let reapplyText = reapplyTime.text {
Now you have the sunscreenText and reapplyText variables, which are typed String, not String? (i.e. they are not optional).
Now, there's these two lines.
sunscreen = sunscreenName.text!
reApplyTime = reapplyTime.text
You don't say which one is giving the error, but the issue is the same in either case. First, use our unwrapped sunscreenText and reapplyText variables instead of sunscreenName.text! and reapplyTime.text. Next, if one of these is meant to be an Int instead of a String, cast it. Swift is not like JavaScript, in that it won't automatically convert values from one type to another, so if something is a string and we need an integer, we have to convert it ourselves.
(assuming reapplyTime was the line that was giving the error:)
if let reapplyInt = Int(reapplyText) {
reapplyTime = reapplyInt
}
The reason we have to unwrap is because Int(String) can return nil if the string is something that can't be converted to an integer. Alternately, we could just provide a default value:
reapplyTime = Int(reapplyText) ?? 0 // sets to 0 if it can't parse the string as an integer

Swift: Convert Int to UInt32, and how to unwrap optional

im pretty new to swift, and im just trying to build something to test out the waters. this is in relation to a previous question i had. I am building some code to take user input from a UITextField object, and basically im trying to figure out how to convert an Int to a UInt32, but nothing ive searched on SO or otherwise has really helped.
here is my code
//this is where i call the user input.
var rangeInput: Int? {
get {
return Int(rangeInputTextField?.text ?? "")
}
//this is my function to create a range, and call a random number out of that range
let viewController = ViewController()
var x = ViewController().rangeInput
let y = (Int?(x!))
var number = arc4random_uniform(Int(y!))//ERROR OCCURS HERE "Cannot convert value of type 'Int' to expected argument type 'UInt32'
//MARK: Class for random number
struct RandomNumber {
// numberRange to change the value for 1...X(user input)
//creates the list to be picked from. (pickRandom)
func numberRange(high: UInt32) ->Range<UInt32>{
if let high = UInt32?(0){
print("Invalid number")
} else { let high = Int(y!) + 1
}
let range = 1...high
return range
}
//pick random number from that list
let pickRandom = number
}
edit: Converted UInt, using answer in comments, but am having an issue with unwrapping optionals.
am I doing something wrong with forcibly unwrapping optionals?
let viewController = ViewController()
var x = ViewController().rangeInput
var number = arc4random_uniform(UInt32(x!)) // <----- UInt32 conversion
However, do recommend to check the user input x and/or after the UInt32 conversion, in case user inputs something not sensible, using guard or if let
Initializers are called in swift by listing the data type (UInt32 in this case) followed by parenthesis containing the parameters. For most basic data types in Swift, there are no parameters for the initializers. So if you ever want to make an Int, Float, Double, UInt32, String etc., just do
UInt32(Value)!//UInt32 can be substituted for any of the above values
The "!" unwraps the optional, and will result in an error in runtime like "unexpectedly found nil when unwrapping optional value" if the value is not a valid form of the data type, if you want to do this safely, you can do this
if UInt32(Value) != nil {
//Valid Value
}else {
//Invalid Value
}
Also this is unrelated to the question, but your if let statement will always be true because you are overriding the value of the parameter "high" to a UInt32 of 0. I could be wrong though.

set value of UISlider with a variable

I have now spent about 12 hours trying to solve this riddle, but I just can't! The truth is I am about 3 weeks into my Swift adventure and this is the first time I have ever written any code (well I think I made a rainbow once on my Atari 800XL!)
I don't really know what I am doing.... I also understand that what I am trying to do here could be fundamentally wrong, and so I will appreciate gift wrapped criticism.
I have a slider - C1
I want to set its value with a variable. However, it wants a float and I can't seem to convert the array I am using to a float. In fact I can't get any of the array values to convert to anything else, and keep getting an error about AnyObject. The code below is one iteration of the trials i have run, all without any luck. It is driving me mad!
The errors are
can't assign a value of int to a value of type float
AnyObject is not convertible to NSNumber; did you mean to use as! to force downcast
This is the code where I get the array from Parse and assign it to NSUSER.
var defaults = NSUserDefaults.standardUserDefaults()
var getAuditId:String = defaults.stringForKey("auditIdGlobal")!
var userId:String = defaults.stringForKey("userIdGlobal")!
var query = PFQuery(className: "auditData")
query.whereKey("auditId", equalTo:getAuditId)
query.findObjectsInBackgroundWithBlock {
(objects: Array?, idError: NSError?) -> Void in
if idError == nil {
if let objects = objects as? [PFObject] {
for object in objects {
var auditId: AnyObject? = object["auditId"]!
var callEhs: AnyObject? = object["ehsData"]!
NSUserDefaults.standardUserDefaults().setObject(callEhs, forKey: "ehsLoad")
This is the code at the user end
var defaults = NSUserDefaults.standardUserDefaults()
var loadArray = defaults.arrayForKey("ehsLoad")!
var test: AnyObject = loadArray[0]
var testFloat = Int(test)
c1.value = testFloat
I appreciate that it shows an int above, but I can't convert to anything!
Xcode asked me to insert a forced downcast to as!NSNumber
var = int(test as! NSNumber)
here I get this error
Could not cast value of type '__NSCFString' (0x10c0bfc50) to 'NSNumber' (0x10c550b88).
(lldb)
if I try to use a forced downcast
var testFloat = test as! Float
On running the app i get the error
Could not cast value of type '__NSCFString' (0x10b690c50) to 'NSNumber' (0x10bb21b88).
(lldb)
Your test variable is a NSString. Try
var test = loadArray[0] as! NSString
and then use
test.floatValue

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)

Resources