What is wrong with this code? Swift optionals - ios

What is wrong with this code?
if documentArray != nil { rowCount = documentArray?.count } else { rowCount = 1 }
Xcode tells me I need to add an ! to the end of count, then when I add it it tells me I need to delete it. This makes no sense to me. I'm ready checking if the NSArray exists so if it does then it should have a count. All this optional crap is really starting to piss me off. What am I doing wrong?

Xcode is mad because you are using optional chaining with documentArray?.count. You should use documentArray!.count to force unwrap the value.
Another approach, conditional binding is sometimes an easier way to not have to worry about these sorts of things.
if let documentArray = documentArray {
rowCount = documentArray.count
} else {
rowCount = 1
}

Related

Checking if array is nil or not [duplicate]

This question already has answers here:
Check if optional array is empty
(8 answers)
Closed 4 years ago.
I have a array of my custom model, and I want to check if it is not nil and its size is greater then 0.
Following is my array with custom object
var listCountries : [Countries]? = nil
now In viewDIdLoad I want to make a check on it. I am new to Swift. I have good experience in working in Java.
I have read out Optional values concept and guard, if let statements. But I am unable to understand how efficiently they may be used. I have read too much SO questions but failed to figure out.
for example , if I want to check the upper given array in java I have only to do
if(listCountries != null && listCountries.size()>0){
//DO something
}
So to summarize my question:
How to make the upper given(Java code) check in to swift 4.? What is more smooth and reliable way.
What is a use of if let , guard, guard let statements. if I declare a variable (array, string) as optional I have to bear optional check like force wrapping each and every place. This is for me so making too much confusion.
Please help. I know this question has been asked in different ways. But this has some different context.
Just use ??.
if !(listCountries ?? []).isEmpty {
However, since you want to probably use listCountries in the if block, you should unwrap
if let listCountries = self.listCountries, !listCountries.isEmpty {
Ideally, if nil and empty means the same to you, don't even use an optional:
var listCountries: [Countries] = []
I would do it something like...
if let list = listCountries, !list.isEmpty { // Can also use list.count > 0
// do something
}
Even though you are not using the list inside the braces you are still using the list in the condition.
Or, like Sulthan said... make it non-optional to begin with if it makes no difference.
Obviously, I would assume that you are able to recognize the difference between nil array and empty array.
So, if we tried to implement a literal translation to your question:
I want to check if it is not nil and its size is greater then 0
For the first condition:
// "I want to check if it is not nil":
if let unwrappedList = listCountries {
// ...
}
and for the second condition:
// "I want to check if it is not nil":
if let unwrappedList = listCountries {
// "and its size is greater then 0":
if !unwrappedList.isEmpty {
// ...
}
}
However, you could combine both of the conditions by using the comma to achieve the multi-clause condition:
// I want to check if it is not nil and its size is greater then 0
if let unwrappedList = listCountries, !unwrappedList.isEmpty {
// ...
}
Or by using guard statement:
// I want to check if it is not nil and its size is greater then 0
guard let unwrappedList = listCountries, !unwrappedList.isEmpty else {
return
}
if let list = listCountries {
if(!list.isEmpty && list.count > 0) {
//value
}
}

segmentation fault 11 in swift2

I have no idea why I get this error.
The problem code is here
for i in 0..<itemDataJson?.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
When I print(itemDataJson?.count) it prints Optional(1).
What am I doing wrong?
Thank you.
It's printing Optional(1) because the variable itemDataJson is nullable, so the count would therefore have to be nullable, because we don't know if itemDataJson actually has a value.
The main problem that I see in your code is that you are force-unwrapping variables. Force-unwrapping a variable is a code smell (usually, although I do it myself from time to time, but you need to be careful).
When you force unwrap a variable, you need to ask yourself the question, "Do I want the app to crash here if this variable is nil?". If the answer is yes, then using a force unwrap is acceptable, otherwise, you should create a non-nullable variable or if that is not possible, you should use the guard statement in swift.
This could be used like this:
guard let itemDataJson = itemDataJson else {
// itemDataJson was null do something
return
}
You can use the if let construct as well, but be careful of the pyramid of doom if you don't use the if let construct correctly. See here for using it correctly, or use a guard statement.
I would recommend checking out the documentation on optionals if you have not done so already.
I would change the code to this version:
if (itemDataJson != nil) {
for i in 0..<itemDataJson!.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}
You should check all optionals before you try to access the underlying value. Best course of action would be to use if let statements.
if let itemDataJSON = itemDataJSON {
for i in 0..<itemDataJSON.count {
if let items = appDelegate.itemDataJSON {
if let imageURL = items[i]["imageURL"].string {
imageUrls.append(imageURL)
}
}
}
}
Other than that, it's a bad practice to store data in AppDelegate. You should create your own data layer that is not dependent of AppDelegate to make data handling more efficient and easier.
instead of checking for nil you should try this.
if let item = itemDataJson {
for i in 0..<item.count {
imageUrls.append(appDelegate.itemDataJson![i]["image_url"].string!)
}
}

variable was written, but never read?

Im getting the following warning variable 'isTaken' was written to, but never read on the following code :
func textFieldShouldEndEditing(textField: UITextField) -> Bool {
var isTaken: Bool = false
if textField == usernameTxt { var query = PFQuery(className: "_User")
query = PFQuery(className: "_User")
query.whereKey("username", equalTo: usernameTxt.text!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) in
if error == nil {
if (objects!.count > 0){
isTaken = true
}
} else {
print("Username is available. ")
}
} else {
print("error")
}
}
}
return true
}
why am I getting the warning and how do I do away with it?
As error says variable 'isTaken' was written to, but never read means you are creating isTaken instance and assigning a value to it but it never used.
Just eliminate the statements:
var isTaken: Bool = false
isTaken = true
Since the value is never used, defining and assigning to it accomplishes nothing.
Basically it's saying that isTaken is assigned a value, but it doesn't actually do anything in your code. You are never using it or checking it's value, so it's simply an warning saying that the variable is unnecessary.
If you actually are using isTaken and the compiler doesn't realize for some reason, you could probably just add another line right after
isTaken = true;
that just says
isTaken;
Or make isTaken global if you're using somewhere else in the code.
Its a compiler warning to point out a dead code. You probably have copy pasted some code and removed some unwanted code. In doing so, usage of local variable isTaken is gone. So, its only being assigned a value and never used for materializing any benefits. You can either simply remove the code around isTaken or double check and put back the functionality around it :).
It's warning you about a var that you set a value, but don't operate over it after.
Is very important keep your code clean and safe, so the xcode just gives you a little help with it.
isTaken = true;
Thats the point you set a value to isTaken variable.
Try to review your code and think about the use of this variable.
I recommend that you do not do this, but the build setting is GCC_WARN_UNUSED_VARIABLE. You can disable it, but again, do not.
I am adding the setting name here because I was searching to find it so that I could check its value in my project, but no question or answer mentioned its name.

Enumerating with a for-in loop

I've got a loop that's throwing an error "Int is not convertible to Range< Int >":
var summaryValues:[Int]=[Int]();
for (dayIndex,valuesPerDay) in enumerate(allValuesPerDay){
if (valuesPerDay>0){
while summaryValues[dayIndex]==nil{ // ERROR
summaryValues.append(0);
}
summaryValues[dayIndex]++
}
}
That fourth line should simply be checking if there is a value at summaryValues in position dayIndex, but I'm a little unclear on the Swift syntax. Any ideas on how to fix this? Thanks for reading.
EDIT:
The work-around I've implemented is to replace the error line with
while cohortRetension.count<dayIndex+1 but I'd still like to better understand why summaryValues[dayIndex] was incorrect.
while summaryValues[dayIndex]==nil{ // ERROR
summaryValues is an array and array[i] won't return nil. It will either return the element or it will crash if your index is out of range. First, the compiler is probably confused from comparing Int and nil. Second, trying to access summaryValues[dayIndex] here will crash, because at the time you are checking it the array is empty and the index does not exist.
Repeatedly appending to an array is inappropriate when you know the size up front. Initialize your summaryValues as:
var summaryValues = [Int](count: allValuesPerDay.count, repeatedValue: 0)
then iterate over your allValuesPerDay simply with:
for dayIndex in 0..<allValuesPerDay.count {
if allValuesPerDay[dayIndex] > 0 {
summaryValues[dayIndex]++
}
}

Which is the correct way to check for null in Swift?

I am supposed to refactor this line of Objective-C code:
if (_currentImageFrame && ![_currentImageFrame.isSaveToLibrary boolValue]) {}
I think of 2 ways to achieve this, but I am not sure which one is better:
1)
if (self.currentImageFrame != nil) &&
(self.currentImageFrame?.isSaveToLibrary == false)
{
}
2)
if let frame = self.currentImageFrame {
if self.currentImageFrame?.isSaveToLibrary == true {
}
}
Please let me know which is more recommended/correct way to achieve it.
Thanks in advance!
I would use the following:
if self.currentImageFrame?.isSaveToLibrary == true {
// Stuff
}
Your var currentImageFrame is ? so you don't have to check if it's nil, in case of nil, it is not going to check the if condition, and if it's not nil it is going to check the if and if the condition is satisfied it will enter. Furthermore you don't need to use parenthesis in Swift :)
Since self.currentImageFrame is an optional, the more compact way of doing what you ask is taking advantage of the way optional chaining works:
if self.currentImageFrame?.isSaveToLibrary == false
{
// Code
}
If self.currentImageFrame is not nil, self.currentImageFrame?.isSaveToLibrary will return the value of isSaveToLibrary.
If self.currentImageFrame is nil, self.currentImageFrame?.isSaveToLibrary will return nil immediately, without attempting to execute anything that's after the ? (no matter how deep the chain goes).
nil does not equal false (unlike Objective-C, in Swift nil only equals nil and nothing else), hence nil == false evaluates to false, so the code behaves as you would expect.
Note that your 1) option would work but it's completely redundant, as the first check is not needed at all.
I think that your 2) option has a typo, and you intended to write false where you typed true. With the typo fixed it would work as well (since you are actually using optional chaining once again inside the outer condition), but it's using optional binding in a wrong way, since you don't use the the bound constant frame inside the outer conditional body. A more correct (although redundant as well) way of using optional binding would be.
if let frame = self.currentImageFrame
{
if frame.isSaveToLibrary == false
{
// Code
}
}
In this case you don't use optional chaining at all. (Although I suspect that the optimized compiled code would be the same in both cases).
Finally, I suggest you rename isSaveToLibrary as it's grammatically incorrect and its meaning is unclear. I would name it wasSavedToLibrary or shouldSaveToLibrary, depending on the intended use.

Resources