In the code I'm not able to remove optional from the value inside the lbltotalamount.
The value in lblTotalAmount is not removing its optional value from it.
Why? The value in grandtotal gets optional removed but when I assign it to a label it returns an optional value again.
The lblTottalAmount is getting an optional value. I want to remove it.
if success == false {
var grandtotal: Any? = value["total"]
if grandtotal != nil {
print("O!O!O!O/\(grandtotal!)")
grandtotal = String(describing: grandtotal)
self.lblTotalAmount.text = ([grandtotal]) as! String // (here I am not able to remove optional)
}
The problem is in the line
grandtotal = String(describing: grandtotal)
You check for nil but you don't unwrap the value so it's still an optional.
And you are misusing String(describing. Never use it for types which can be converted to String with an init method.
Use always conditional downcast
if success == false {
if let grandtotal = value["total"] as? Double {
self.lblTotalAmount.text = String(grandtotal)
}
}
Related
I check the optional string
print(limitCash)
if let value = Int32(limitCash) {
aProvider.limitBuy = value
}
The value of limitCash is Optional("500").
The program checks if let statement and skips it without assigning value.
Program crashes if I try aProvider.limitBuy = Int32(limitCash)!
First you need to unwrap String? to String and then unwrap the result of casting from String to UInt32 (that will be Uint32?).
print(limitCash)
if let stringValue = limitCash {
if let value = Int32(stringValue) {
print(value) // 500
}
}
I am trying to read a string and convert it to an int. I have a solution but it seems way too complicated. I guess I am still trying to wrap my head around unwrapping.
I have posted code below along with the compiler errors that I get with each solution.
In this example I try to read a string from UserDefaults and convert to an integer value.
static func GetSelectedSessionNum() -> Int32 {
var sessionNum : Int32 = 0
let defaults = UserDefaults.standard
let optionalString: String? = defaults.string(forKey: "selectedSessionNum")
// this works but it's too complicated
if let string = optionalString, let myInt = Int32(string) {
return myInt
}
return 0
// Error : optional String? must be unwrapped to a value of type 'String'
let t : String = defaults.string(forKey: "selectedSessionNum")
if let s : String = defaults.string(forKey: "selectedSessionNum") {
// error - Int32? must be unwrapped to a value of Int32
return Int32(s)
}
return 0
}
You need to cast to non optional Int32 in order to match your return type.
You can use any optional binding approach, or change your return type to Int32?
If you want an uncomplicated solution save selectedSessionNum as Int
static func getSelectedSessionNum() -> Int32 {
return Int32(UserDefaults.standard.integer(forKey: "selectedSessionNum"))
}
otherwise double optional binding
if let string = UserDefaults.standard.string(forKey: "selectedSessionNum"), let myInt = Int32(string) {
return myInt
}
or the nil coalescing operator
if let string = UserDefaults.standard.string(forKey: "selectedSessionNum") {
return Int32(string) ?? 0
}
is the proper way
If you want to avoid optional bindings, you can use flatMap, when called on Optional's it allows you to convert one optional to another:
return UserDefaults.standard.string(forKey: "selectedSessionNum").flatMap(Int32.init) ?? 0
You'd also need the ?? (nil coalescing operator) to cover the scenarios where either the initializer fails, or the value is not present in user defaults.
So here I have a basic setup
var preferenceSpecification = [String : String?]()
preferenceSpecification["Key"] = "Some Key"
preferenceSpecification["Some Key"] = nil
preferenceSpecification["DefaultValue"] = "Some DefaultValue"
print(preferenceSpecification)
var defaultsToRegister = [String : String]()
if let key = preferenceSpecification["Key"], let defaultValueKey = preferenceSpecification["DefaultValue"] {
defaultsToRegister[key] = preferenceSpecification[defaultValueKey]!
}
But the error points out where it demands that I force unwrap this, to be like this:
defaultsToRegister[key!] = preferenceSpecification[defaultValueKey!]!
Which doesn't make sense, because keyValue and defaultValue already are unwrapped
When you extract a value from a dictionary like this using subscript
[String: String?]
you need to manage 2 levels of optional. The first one because the subscript returns an optional. The second one because the value of you dictionary is an optional String.
So when you write
if let value = preferenceSpecification["someKey"] {
}
you get value defined as an optional String.
Here's the code to fix that
if let
optionalKey = preferenceSpecification["Key"],
key = optionalKey,
optionalDefaultValueKey = preferenceSpecification["DefaultValue"],
defaultValueKey = optionalDefaultValueKey,
value = preferenceSpecification[defaultValueKey] {
defaultsToRegister[key] = value
}
Suggestions
You should avoid force unwrapping as much as possible. Instead you managed to put 3 ! on a single line!
You should also try to use better name for your constants and variables.
You could also define an extension which helps get rid of the double optional situation.
extension Dictionary where Value == Optional<String> {
func flattened(_ key: Key) -> Value {
if let value = self[key] {
return value
}
return nil
}
}
Usage: preferenceSpecification.flattened("someKey")
I knew that safely unwrapping is as follows
var firstName:String?
if let firstName = firstName
{
dictionary.setObject(firstName, forKey: "firstName")
}
else
{
dictionary.setObject("", forKey: "firstName")
}
I want to add firstname to dictionary even it is nil also. I will have 10-15 var's in dictionary.
I do not want to check for this condition 10-15 times for ear var.
I am having more than 1000 optionals through out project.
So thought of writing writing a func will help me duplicate the code and reduce the number of lines.
So implemented this func as below.
func checkNull(str:String) -> String
{
return str.characters.count > 0 ? "\(str)" : ""
}
but while calling the func,
let addressstr = self.checkNull(address?.firstName)
firstname is the var in address model here.
The auto correction sugguests
let addressstr = self.checkNull((address?.firstName)!)
The above line causes the crash.
First of all firstName is an Optional therefore you cannot pass it to a function which only takes String.
In addition this line:
str.characters.count > 0 ? "\(str)" : ""
Is equivalent to just returning str so you don't check whether it is an Optional.
Solution
In this case it is way easier to use the nil coalescing operator:
let addressstr = address?.firstName ?? ""
If address is not nil firstName gets unwrapped and bind to addressstr. Otherwise this string gets assigned to it: ""
My problem is that I have some text fields that the user enters in numbers, the entered numbers then get saved to the corresponding variable.
However if the user doesn't enter a number and leaves it blank, the text field has a value of 'nil' and so would crash if unwrapped.
So I used an if statement to only unwrap if the contents of the test field are NOT nil, however this doesn't work. My program still unwraps it and crashes because the value is nil...
I don't understand how my if statement is not catching this.
On another note, how do I change my if statement to only allow Int values to be unwrapped and stored, strings or anything else would be ignored.
#IBAction func UpdateSettings() {
if CriticalRaindays.text != nil {
crit_raindays = CriticalRaindays.text.toInt()!
}
if EvapLess.text != nil {
et_raindays_lessthan_11 = EvapLess.text.toInt()!
}
if EvapMore.text != nil {
et_raindays_morethan_11 = EvapMore.text.toInt()!
}
if MaxWaterStorage.text != nil {
max_h2Ostore = MaxWaterStorage.text.toInt()!
}
if CarryForward.text != nil {
carry_forward = CarryForward.text.toInt()!
}
}
Your issue is that while the text exists, it doesn't mean toInt() will return a value.
Say the text was abc, CriticalRaindays.text != nil would be true but CriticalRaindays.text.toInt()! can still be nil, because abc cannot be converted to an Int.
The exact cause of your crash is likely that .text is equal to "", the empty string. It's not nil, but definitely not an Int either.
The better solution is to use optional binding to check the integer conversion and see if that passes, instead of merely the string existing:
if let rainDays = CriticalRaindays.text.toInt() {
crit_raindays = rainDays
}
If that doesn't compile, you possibly need to do Optional chaining:
if let rainDays = CriticalRaindays.text?.toInt()
Not on a Mac atm so can't test it for you but hope this makes sense!
Why not use an if let to unwrap on if the the text field's text is non-nil?
if let textString = self.textField.text as String! {
// do something with textString, we know it contains a value
}
In your ViewDidLoad, set CarryForward.text = "" This way it will never be nil
Edit:
To check if a textfield is empty, you can use this:
if (CarryForward.text.isEmpty) {
value = 10
}
else {
value = CarryForward.text
}