Swift when string is null gives fatal error - string

I have some string codes but when my strings values null gives my app fatal error my codes here which changes i need to do ?
let pCodeTextFieldStr:NSString = pCodeTextField.text!
let pNameTextFieldStr:NSString = pNameTextField!.text!
let pQuantityTextFieldStr:NSString = pQuantityTextField.text!
let commingReadyIDs:NSString = prefs.valueForKey("addClientID") as! String!
let commingCurrs:NSString = prefs.valueForKey("addClientCurrency") as! String!
let commingtype:NSString = prefs.valueForKey("addProductType") as! String!
let productnameclean:NSString = prefs.valueForKey("addProductName") as! String!

The exclamation mark ! tells the compiler "if this item is nil then please crash". So the first step would be not to do that. The second step would be to figure out what you want to do if something is nil. You usually handle this using if let ... or using the nil-coalescing operator, ??.

They aren't "opinion"s they're how it is.
let commingCurrs:NSString states that commingCurrs CAN NEVER be nil.
Likewise, the fragment prefs.valueForKey("addClientID") as! String! states that prefs.valueForKey("addClientID") CAN NEVER return nil.
Since your data obviously CAN be nil, you're going to have to resolve the discrepancy.
You've been given 3 options, the only possible other option is to declare your variables as optional instead of required:
let commingReadyIDs:String? = prefs.valueForKey("addClientID") as? String
But that's just kicking the can down the road. Sooner or later you're going to have to deal with the fact that these values WILL BE NIL at some point.
Just a note, ANY time you're posed with the option of putting ! in a swift program you need to think long and carefully about is it really the right thing to do? Can this truly NEVER be nil? ! is the most evil, insidious, character in the swift language.

Since there's a possibility that these string values do not exist, you should not force unwrap them. An optional represents a variable that may have a value or it may have nil. Swift encourages you as a developer to acknowledge this and unwrap cases where a value may return nil. By using !, you are telling the compiler, I know there's a value here, in which in your case, there was not and an exception was thrown.
In the example below, if a string exists for the key, it will enter the scope of the block and you can access the string with the constant variable, commingReadyIDs. If not, the block will not be entered and no exception will occur.
I would encourage you to read more about optionals in Swift documentation since you will frequently encounter them and they encourage developers to avoid these null pointer exceptions.
if let commingReadyIDs = prefs.valueForKey("addClientID") as? String{
//do whatever
}

If you don't know when value exists of a variable or when not then you should use Optional Chaining, you can try below code
// all defined variables (like pCodeTextField) can have value or can be nil so don't use forcefully unwrapped optional value without cheking nil
let pCodeTextFieldStr:NSString? = pCodeTextField?.text
let pNameTextFieldStr:NSString? = pNameTextField?.text
let pQuantityTextFieldStr:NSString? = pQuantityTextField?.text
let commingReadyIDs : NSString? = prefs?.valueForKey("addClientID") as? String
let commingCurrs : NSString? = prefs?.valueForKey("addClientCurrency") as? String
let commingtype : NSString? = prefs?.valueForKey("addProductType") as? String
let productnameclean : NSString? = prefs?.valueForKey("addProductName") as? String

Related

RunTime Error when trying to take String input using readLine in Kotlin

I am a beginner in kotlin and this is the code I tried to execute, but runtime error is being displayed. Please help me resolve this.
import java.util.*
fun main(args: Array<String>)
{
var inp = Scanner(System.`in`);
var t:Int = inp.nextInt();
repeat(t)
{
var n:Int = inp.nextInt();
var s:String = readLine()!!
for (i in s)
{
println(i);
}
println()
}
}
Exception in thread "main" kotlin.KotlinNullPointerException
at ProgKt.main(prog.kt:10) This is the error that is displayed.
Smeki's answer is right, but I just need to point something out since you're a beginner and it might get confusing.
Normally you'd do something like this:
val s = readLine()
Notice you're not specifying the type of s - it's being inferred by whatever you're assigning to it. Because readLine returns a nullable String?, which is a String that could be null (which is what the ? on the end means), then the compiler knows that s is a String?. It's the equivalent of doing this:
val s: String? = readLine()
And you can do that explicitly if you want! You usually don't need to though. And now you have your nullable s, you can do some null-checking to use it safely:
if (s != null) {
// we know it's not null, so now you can do stuff with it
} else {
// if you like, you can handle the null case separately, like breaking out of
// the loop (since null from readLine() means you've reached the end)
}
There's other ways to handle nulls and do null-checking - here's the documentation about it and I'd strongly recommend reading it and getting your head around it - it's a key part of the language! And it makes your life easier and code safer in the long run (avoids problems like this! !! gets around null-safety and it's usually a bad sign)
But remember when I said you can explicitly declare the type for s? Here's what I said it would be, and what you've written:
// correct
val s: String? = readLine()
// something's different!
val s: String = readLine()
See how you're missing the ? that says its a nullable type? Even if you're planning to null-check s after this, it's going to crash at this line because s is declared as a non-null type, and readLine() is gonna to be null at some point. When you assign null to a non-null variable, it'll crash with an error - because as far as the compiler's concerned, something's gone wrong.
(You should also get some warnings in your IDE if you're using one, trying to null-check a variable that you've declared as non-null will give you some "why are you trying to do this? It can't be null, right?" messages that hint that something's wrong somewhere. Also if you didn't add the !! after readLine(), you'd get a warning about that - probably why you added the !! in the first place! It doesn't make the problem go away, just stops the IDE from warning you about it)
Also you might have noticed, I made s a val instead of a var because it's a fixed value you're not going to change - always prefer vals unless you definitely need to change that variable, it's not such a big deal here but it makes some other things easier (you'll get warnings about that too)
Well, NPE is the most probably thrown from
var s:String = readLine()!!
where those !! are part of kotlin null-safety feature.
And from java doc of readLine() we can find out when is null returned.
/**
* Reads a line of input from the standard input stream.
*
* #return the line read or `null` if the input stream is redirected to a file and the end of file has been reached.
*/
fun readLine(): String? = LineReader.readLine(System.`in`, Charset.defaultCharset())

get() function doesn't yield any result on tuple keys, and the values exist and do match when manually checked

let mut FEED_CA_:HashMap<(Address, Address, Address), (iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>)> = HashMap::new();
for i in FEED_CA_START{
let c0 = i[0];
let c1 = i[1];
let c2 = i[2];
let p_01_ = ALL_C.get(&&(*c0, *c1)).ok_or(continue).unwrap();
FEED_CA_START is a vec of (c0, c1, c2) tuples :)
ALL_C is a HashMap of <(Address, Address), Address> from which I can't get the value out.
The whole iteration fails on last line in the above snippet. There's eight more of those get()s, and not one of them gets filled, the output data structure (FEED_CA_ ) to which I insert results after iterating through the whole db is always len = 0.
I've:
Printed out the values &(c0, c1) for each i in my tuple FEED_CA_START
Printed out the values of the faulty &(c0, c1), as well as &(*c0, *c1) and even &&(*c0, *c1).
Manually found that they indeed do match, do exist (100% of those calls should succeed).
Printed out the types of the above and made sure they indeed do match. Same result, even if types of the query and key match and are both exactly: &(primitive_types::H160, primitive_types::H160)
Done some borrow&deref trial-and-error combinations, there's only so many I can try, and still no hope.
printed out the databases I've derived those from and manually checked - there's multiple matches, and the program returns exactly 0 always, and I've tried multiple functions/methods/iterations, my program always breaks at get() function, goes straight to continue.
I ran a simple test where in a new program I've created a HashMap with addresses akin to ALL_C , and accessed it using a tuple key - and there it worked. Here it doesn't. I've even thrown some reference/typing spaghetti there to make sure it breaks as well, it didn't. Got the value every time. Here I'm trying as hard as I can to make it work, and it's day 2 stuck on this issue, dead end.
How do I approach a problem like that? I'm lost.
If I've understood the code snippet is correctly, I think your problem is the .ok_or(continue).
Arguments passed to ok_or are eagerly evaluated; if you are passing the result of a function call, it is recommended to use ok_or_else, which is lazily evaluated.
In other words, the continue is always evaluated and so the code is really not doing what you expect.
let p_01_ = ALL_C.get(&(c0, c1)).ok_or(continue).unwrap();
println!("unreachable");
As a possible solution, you could use filter_map which will ignore all None results and then check the length. It doesn't seem very elegant but it might be better that repeated if let blocks:
// Note: I just guessed some types for an example
for i in FEED_CA_START {
let c0 = i.0;
let c1 = i.1;
let c2 = i.2;
let args = vec![(c0, c1), (c0, c2)];
let ps: Vec<&Address> = args.iter().filter_map(|arg| ALL_C.get(arg)).collect();
if ps.len() != args.len() {
continue;
}
// Do something
}
As per the other answer, continue is evaluated so it never get to check the other code. IMO it should be imposible to construct that in the first place.
On the other hand, it is not a good construct to do that, you should use something more rusty like:
let mut FEED_CA_:HashMap<(Address, Address, Address), (iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>, iuniswapv2pair_mod::IUniswapV2Pair<Provider<Ws>>)> = HashMap::new();
for i in FEED_CA_START{
let c0 = i[0];
let c1 = i[1];
let c2 = i[2];
if let Some(p_01_) = ALL_C.get(&&(*c0, *c1)) {
// compute stuff over `p_01_`
} else { // else could be avoided if you don't need to compute anything else in the loop if there is nothing for the key
continue
};
...

Get [String] from NSUserDefaults.standardUserDefaults().dictionaryRepresentation().keys

NSUserDefaults.standardUserDefaults().dictionaryRepresentation().keys returns [NSObject] but I need (and would expect) [String]. Is it just some coffee I'm missing?
dictionaryRepresentation() returns NSDictionary that does not support generic types. You can convert to Swift dictionary:
let dictionary = NSUserDefaults.standardUserDefaults().dictionaryRepresentation() as [String : AnyObject]
let keys = dictionary.keys
That actually returns a LazyBidirectionalCollection<MapCollectionView<Dictionary<Key, Value>, Key>>. You can add .array to get an array instance back, then use map to cast the values to String:
let keys = NSUserDefaults.standardUserDefaults().dictionaryRepresentation().keys.array.map { $0 as String }
println(keys)
// [NSLanguages, NSInterfaceStyle, AppleLanguages]

In Swift: the example below why must I place (let one) next to .Success for the code to work?

In the example below why must I place (let one) next to .Success for the code to work? Why is it not (let = one) or just (one)? I am trying to understand the syntax.
enum Status {
case Success(String)
case Failure(String)
}
let downloadStatus = Status.Failure("Network connection unavailable")
switch downloadStatus {
case .Success(let one):
println(one)
case .Failure(let two):
println(two)
}
This is known as value binding. The value contained in the enum value is temporarily bound to the variable in the case. If you use let the value will be bound to a constant and if you use var it will be bound to a variable.
let downloadStatus = Status.Failure("Network connection unavailable")
switch downloadStatus {
case .Success(let one):
println(one)
case .Failure(var two):
two += "!!!" // two is a var, so I can modify it
println(two)
}
I am trying to understand the syntax
There is nothing to understand. That simply is the syntax for extracting the associated value from an enum case. It's like the masks when you play Calvinball - no one is allowed to question them.
Now, you might object that this syntax is kind of clumsy and annoying. You have to go through the rigmarole of a switch statement just to pull out the associated value?!? That is a perfectly valid complaint, people have been making it since Swift was unveiled in June 2014, and the folks at Apple know that it's kind of weird and may change it in future. But for now, that's the syntax, and that's all there is to know about it.

'(NSObject, AnyObject)' is not convertible to 'String'

How do I convert an object of type (NSObject, AnyObject) to the type String?
At the end of the first line of the method below, as String causes the compiler error:
'(NSObject, AnyObject)' is not convertible to 'String'
Casting street to NSString instead of String compiles, but I'm casting street to String because I want to compare it to placemark.name, which has the type String!, not NSString.
I know name and street are optionals, but I'm assuming they're not nil for now because all the places returned from MKLocalSearch seem to have non-nil names and streets.
func formatPlacemark(placemark: CLPlacemark) -> (String, String) {
let street = placemark.addressDictionary["Street"] as String
if placemark.name == street {
// Do something
}
}
A String is not an object, so you do need to cast it to an NSString. I would recommend the following syntax to cast it and unwrap it at the same time. Don't worry about comparing it to a variable of type String! since they are compatible. This will work:
func formatPlacemark(placemark: CLPlacemark) -> (String, String) {
if let street = placemark.addressDictionary["Street"] as? NSString {
if placemark.name == street {
// Do something
}
}
}
This has the added benefits that if "Street" is not a valid key in your dictionary or if the object type is something other than NSString, this will not crash. It just won't enter the block.
If you really want street to be a String you could do this:
if let street:String = placemark.addressDictionary["Street"] as? NSString
but it doesn't buy you anything in this case.
The return type from looking up via subscript for a swift dictionary has to be an optional since there may be no value for the given key.
Therefor you must do:
as String?
I think it may have to do with addressDictionary being an NSDictionary.
If you convert addressDictionary to a Swift dictionary, it should work.
let street = (placemark.addressDictionary as Dictionary<String, String>)["String"]

Resources