Swift - Array Filter not removing objects - ios

I have an Array of Strings as shown:
I also have an array of objects that contain:
When I run the following line:
let filterNo = self.responseObjs.filter({!formItemIds.contains(String(describing: $0.formItemId))})
I expect filterNo to be empty as all the formItemIds are contained in the array. It is however not removing any of the items. Am I missing something basic?

Remove the describing from the init of String and use Nil-Coalescing Operator with $0.formItemId to unwrapped optional.
let filterNo = self.responseObjs.filter({!formItemIds.contains(String($0.formItemId ?? 0))})
You are not getting filtered data because your formItemId property is optional and using String(describing: $0.formItemId) give you output like Optional(98)

Related

Why does randomElement() return ?? and not?

I'm learning Swift and some iOS development. Here is my code which I'm trying to figure out why XCode is complaining.
private func rollMyDice() {
let diceRollImages = [
UIImage(named: "DiceOne"),
UIImage(named: "DiceTwo"),
UIImage(named: "DiceThree"),
UIImage(named: "DiceFour"),
UIImage(named: "DiceFive"),
UIImage(named: "DiceSix")
]
diceOneImageView.image = diceRollImages.randomElement()
diceTwoImageView.image = diceRollImages.randomElement()
}
So in this case, diceOneImageView.image = diceRollImages.randomElement() will complain that it cannot assign UIImage?? to UIImage?.
ImageView.image is of type UIImage?. What I don't understand is why is randomElement() here is returning UIImage??.
What does UIImage?? mean? I thought ?? was the nil coalescsing operator, so I'm not sure why it's part of some return type.
Also reading the documentation on randomElement(), it should return ?. So in this case, I would expect diceRollImages.randomElement() to return UIImage? which should suit diceOneImageView.image.
What is happening here? I know I can fix it by using ! or using nil coalescing etc. to make it work. Just don't get what's going on.
Note that UIImage(named:) is a failable initialiser, and so the expressions UIImage(named: "DiceOne") etc are of type UIImage?. This makes the array diceRollImages of type [UIImage?]. Each Element of the array is UIImage?, alternatively written as Optional<UIImage>.
As you may know, Optional is just an enum with two cases, .some and .none (aka nil). Each element of the array can be:
.some(image), if a UIImage is created successfully from the name
.none, if there is no images with that name
randomElement is declared to return Element? (Optional<Element>), because the array could have no elements, and hence, cannot give you a random element. randomElement returns:
.none, when the array is empty
.some(elementOfTheArray), when the array is non-empty and elementOfTheArray is a random element in array.
Recall that Element is Optional<UIImage> in the case of diceRollImages, and that the array elements (elementOfTheArray) could have values .some(image) or .none.
Therefore, we can say that randomElement returns a value of type Optional<Optional<UIImage>>, aka UIImage??, and it could be one of 3 things:
.none when the array is empty
.some(.some(image)) when the array is non-empty and a successfully-created image in the array is randomly selected
.some(.none) when the array is non-empty and a unsuccessfully-created image in the array is random selected
Since you are hardcoding the array of images, you know that the array is not empty, and so it is safe to force-unwrap the outer layer of the optional:
diceOneImageView.image = diceRollImages.randomElement()!
The answer has been given already in the comments: UIImage(named:) returns (optional) UIImage? and the result of calling randomElement() on an optional is a double optional ??
This is a good example where force unwrapping is welcome.
The images are part of the application bundle which is immutable at runtime and the app is useless if one of them is missing.
Declare diceRollImages
let diceRollImages = [
UIImage(named: "DiceOne")!,
UIImage(named: "DiceTwo")!,
UIImage(named: "DiceThree")!,
UIImage(named: "DiceFour")!,
UIImage(named: "DiceFive")!,
UIImage(named: "DiceSix")!
]
If the code crashes nevertheless it reveals a design mistake which can be fixed immediately.
You can even force unwrap randomElement()! because the array is a constant and is clearly not empty.

Appending items to an array in dictionary not working

I have this static dictionary created as so:
static var pictures = Dictionary<Int, Array<UIImage>>()
I want to populate it with images. At the moment when I am creating it I don't know how many key/value pairs I need to create. I have to fetch from the internet the data, but after that I am doing this to populate, but still my dictionary is empty:
for i in 0...Fetching.numberOfAliveListings - 1 {
for _ in 0...AdsCollectionView.listings[i].photos.count - 1 {
AdsCollectionView.pictures[i]?.append(UIImage(named: "noimage")!)
}
}
pictures is initially empty. So any attempt to access a value for a given key will result in a nil value. Since the value (the array) is nil, the optional chaining skips the call to append.
One solution is to provide a default array when looking up the value for a given Int.
AdsCollectionView.pictures[i, default: []].append(UIImage(named: "noimage")!)
You may also wish to consider alternate syntax when declaring pictures:
static var pictures = [Int: [UIImage]]()

Ambiguous use of 'mutableCopy()' Swift3

I tried to update Swift 3 and I got the following error :
Ambiguous use of 'mutableCopy()'
Before update to swift 3. It runs well.
Swift 2.3
NSUserDefaults.standardUserDefaults().objectForKey("listsavednews")?.mutableCopy() as! NSMutableArray
Swift 3.0
(UserDefaults.standard.object(forKey: "listsavednews")? as AnyObject).mutableCopy() as! NSMutableArray
I found that mutableCopy in Swift3 return Any that doesnt have method mutableCopy() so that it needs to cast to AnyObject.
Any helps thanks.
I dont know why I can't comment.
Thanks all, I'll be using :
UserDefaults.standard.mutableArrayValue(forKey: "listsavednews")
mutableCopy is an Objective-C method from NSObject. There's little reason to use it in Swift 3.
Since you are dealing with UserDefaults and mutableCopy, you must be dealing with either an array or dictionary. Or it could be a string.
The proper way to do this in Swift 3 is to use the proper UserDefaults method to get an array or dictionary. And assign the result to a var. That combination will give you a mutable array or mutable dictionary.
var someArray = UserDefaults.standard.array(forKey: "somekey")
or:
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey")
In the two above cases, you end up with an optional since there might not be any data for the given key. And you also get a non-specific array or dictionary which isn't ideal. It would be better to cast the result to the appropriate type.
Let's say you have an array of strings and you want an empty array if there is nothing currently in user defaults. You can then do:
var someArray = UserDefaults.standard.array(forKey: "somekey" as? [String]) ?? []
Adjust as necessary if the array contains something other than String.
If you actually have a dictionary, the code would be similar.
var someDictionary = UserDefaults.standard.dictionary(forKey: "somekey") as? [String:String] ?? [:]
If your original object is just a string, then you could do:
var someString = UserDefaults.standard.string(forKey: "somekey") ?? ""

NSUserDefaults properly storing Strings in SpriteKit Swift

So I set up a NSUserDefault to store a string in my GameViewController
NSUserDefaults.standardUserDefaults().setObject("_1", forKey: "SkinSuffix")
The idea is it stores a suffix which I will attach to the end of an image name in order to save what skin of a character the player should use.
When I call the value in my GameScene class like so
var SkinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix")
println(SkinSuffix)
it prints "Optional("_1")" instead of just "_1" so when I try to change the name of my image file like so, it doesn't load the image file
hero = SKSpriteNode(texture: heroAtlas.textureNamed("10Xmini_wizard\(SkinSuffix)"))
How do I fix this issue?
You can unwrap the String using the Optional Binding construct. This avoids a crash of the app if the value is nil.
if let skinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix") {
println(skinSuffix)
}
Update: As correctly suggested in the comment below, I am putting the retrieved value in a constant (let). We should always use constants when we don't need to change the value. This way the Swift compiler can make some optimizations and does prevent us from changing that value.
That's because it's implicitly an optional not of type String. You need to case it as such or unwrap the optional in your println statement.
var SkinSuffix = NSUserDefaults.standardUserDefaults().stringForKey("SkinSuffix") as! String
Or in your println: println(SkinSuffix!)
As a side note, you should you camelCase for your variable names.
You can use "??" Nil Coalescing Operator to help you dealing with nil and it also
allows you to specify a default string value.
NSUserDefaults().setObject("_1", forKey: "SkinSuffix")
let skinSuffix = NSUserDefaults().stringForKey("SkinSuffix") ?? ""
println(skinSuffix) // "_1"

trying to populate a cell label with a string from an array

So, I have a cell with one label inside. I am trying to populate that label text with the various items in my array - all strings.
My array
var restauranttypelist: [String] = ["American", "Asian", "Bakery & Deli",
"Burgers", "Italian", "Mexican", "Seafood", "Steakhouse"]
and my cell label text
let type = restauranttypelist [indexPath.row]
var typecell = tableView.dequeueReusableCellWithIdentifier("cellone") as RestaurantTypeCell
typecell.restaurantTypeLabel.text = restauranttypelist.text
return typecell
I have tried a number of solution ranging from ".text" seen above, to ".String", to "restauranttypelist:indexPath.row" to no avail.
I suppose I have two questions. Am I setting up my array correctly? Do I need to insert the "[String]" portion after the variable name?
Finally, how would I be able to set the cell label to the numerous items I have in my array?
Thanks for any help... beginning.
Jon
In let type = restauranttypelist[indexPath.row] you're accessing a String from your Array and storing it in type. Therefore, all you need is typecell.restaurantTypeLabel.text = type.
There's nothing wrong with how you setup the array. You don't need the [String] type annotation since it can be inferred from the value you are assigning to it, but having it there does no harm.
Finally, this doesn't affect how your code works, but it's nice to know anyway:
Swift variable names follow the convention of starting with a lowercase character, and then capitalizing every subsequent word. Following that convention your variable names should be typeCell and restaurantTypeList.

Resources