How to replace dictionary particular key value using Swift - ios

I am implementing button click to add CollectionView Cell like a Tag UI. Here, I created TagModel class for maintaining TagId and validating each time the tag data available or not by using if items.contains(tag) == false {….} (It will avoid duplication into collection view). Now, I need to add one more validation into this, if items.contains(tag) == true need to check tagName and current selected values are same. if not same I need to replace that tag value. How to achieve this?
Tag Validation Code
func tagValidation(){
// Validate A
if let aValue = UserDefaults.standard.object(forKey: "a") as? [String:Any] {
let tag = TagModel(dict: aValue)
if items.contains(tag) == false { // how to check if true need to validate current value and already exists values are same. if same no need to replace or else need to replace value
items.append(tag)
}
}
}
First ViewController
#IBAction func saveAction(_ sender: Any) {
let tag = TagModel(tagId: 0, tagName: "test", tagStoreKey: "a")
tag.save()
self.dismiss(animated: true, completion: nil)
}

Something is not right with your equality operator: you are asking if two items are the same - and if they are the same then check if something in them is different :)
Try to search array with first method: if you find (and you will) any item matching the case continue with your work.
Something like this:
if let aValue = UserDefaults.standard.object(forKey: "a") as? [String:Any] {
let tag = TagModel(dict: aValue)
if items.contains(tag) == false {
items.append(tag)
} else if let existing = items.first(where: { $0 == tag}), existing.tagName != tag.tagName {
// replace item
let index = items.firstIndex(of: tag)!
items[index] = tag
}
}

Related

UICollectionView ReloadData not showing content properly

I am trying to filter my homeCollectionView with SegmentControl. On taping on the segment, I am filtering the content based on tag available in the dictionary. When I am performing ReloadData and switching between the segments, in the first go (when I am taping on the segments the first time), the filter is working and all data is coming, but when I tap back on the segments, part of the content in the cell, especially the LabelViews text are not showing up afterwards. Also, it's happening for random indexPath.
This is my code:
#objc func toggleHomeContent(_ notification: NSNotification) {
toggleValContType = notification.object as? String ?? "all"
if (toggleValContType == "all") {
mainArrayData = primaryArrayData
}
else if (toggleValContType == "collections") {
mainArrayData = primaryArrayData { $0["filterType"] == "Col" || $0["filterType"] == "CTA" }
}
else if (toggleValContType == "books") {
mainArrayData = primaryArrayData { $0["filterType"] == "Book" || $0["filterType"] == "CTA" }
}
homeCollectionView?.reloadData()
homeCollectionView?.layoutIfNeeded()
homeCollectionView?.collectionViewLayout.invalidateLayout()
//DispatchQueue.main.async(execute: homeCollectionView.reloadData)
}
And by arrays are declared like this:
var mainArrayData : [[String:String]] = HomeArray().mainArray
var primaryArrayData: [[String:String]] = HomeArray().mainArray
Heres the snapshot of what the issue is:
Snapshot of the issue
Thanks in advance!
Add this code in main thread :
homeCollectionView?.reloadData()
homeCollectionView?.layoutIfNeeded()
DispatchQueue.main.async {
homeCollectionView?.reloadData()
homeCollectionView?.layoutIfNeeded()}

How can I check if the text of a button contains an element of an array?

I'm pretty new to this so bear with me. What I'm trying to do is check if the button is currently displaying an element from an array; array Answers. What would be the best route to do so?
#IBAction func qButton1(_ sender: Any) {
if (sender as AnyObject).currentTitle == (Answer in Answers){
PickQuestion()
}
else{
missedWords.append(quizLabel.text!)
}
}
Not sure why you pass Any as sender and then cast to AnyObject. Anyway I would use filter in your case:
Suppose you have an array of Answer objects called answers and Answer has a property title
if let _ = answers.filter( { $0.title == (sender as AnyObject).currentTitle) }).first {
PickQuestion()
} else{
missedWords.append(quizLabel.text!)
}
You should not use the button's title to store data and compare it to a value.
Any time you write code who's logic depends on text displayed to the user you are paining yourself into a corner in terms of localization.
Using a numeric tag to index into an array of strings would be a much better idea.
Say you create an array of title strings.
Make each button's tag be 100 + the array index.
let startingTag = 100
#IBAction func qButton1(_ sender: UIButton) {
guard let index = sender.tag where startingTag >= 100 else {
return
}
//fetch your string from the array of button title strings you set up.
let stringToFind = strings[index]
if answers.contains(where: {$0 == stringToFind}) {
//Found the current' buttons' string in answers
} else {
//string not found
}
}

If filter returns nil, how do I not return empty result?

I'm doing the following filtering that returns a recipe list, filtered by given category name value.
filteredRecipe = filteredRecipe.filter({
if let category = $0.valueForKey("category") as? NSManagedObject {
if let name = category.valueForKey("name") as? String {
return name.rangeOfString(cap) != nil
} else {
return false
}
} else {
return false
}
})
This filter works in association with searchBar textfield so I will possibly have random value in the textfield which will make filteredRecipe to hold unreliable data.
I need to make sure when the filter can't find any recipe from filteredRecipe with given category name value, I leave filteredRecipe untouched.
Currently, when there is no match, it makes filteredRecipe empty array []. I'm not sure what part causes this.
You need to assign the filtering result to a temporary variable and check that it isn't empty.
let filtered = filteredRecipe.filter({
if let category = $0.valueForKey("category") as? NSManagedObject {
if let name = category.valueForKey("name") as? String {
return name.rangeOfString(cap) != nil
}
return false
})
if !filtered.isEmpty {
filteredRecipe = filtered
}
Another approach is to extend Collection with a selfOrNilIfEmpty property, and use the nil coalescing operator:
extension Collection {
var selfOrNilIfEmpty: Self? {
return isEmpty ? nil : self
}
}
and later, on your code:
let filteredRecipe = filteredRecipe.filter { ... }.selfOrNilIfEmpty ?? filteredRecipe

Better way to check for a Int in a textbox

I am learning Xcode and creating a very simply program with a textbox(txt box) in which the user enters a value, a button (btnCalc) that performs a calculation, and a label (lblcalcdnumber) that shows the calc'd number. I have already selected Number Pad to be displayed as the dropdown keyboard but i want to check to make sure that if they enter anything other than a number that nothing happens. The code i have works but i feel like there should be a cleaner solution. Essential i want them to only enter Integers in the textbook.
// Mark: Actions
#IBAction func btnCalc(sender: UIButton) {
// let txtbox text beome int
let number1 = Int(txtBox.text!)
// let possibleInt convert mystring to int to check for nil, txtbox becomes OPTIONAL
let possibleInt = Int(txtBox.text!)
let number = 25
if possibleInt != nil {
let combinednumber = "\(Int(number1!) * number)"
lblCalcedNumber.text = combinednumber
}
else {
txtBox.text = ""
txtBox.placeholder = "Please Enter a Valid Number"
}
}
You can use if and let together to create an optional. If the value is not nil it will cast to possibleInt, otherwise, it will evaluate as false.
#IBAction func btnCalc(sender: UIButton) {
if let possibleInt = Int(txtBox.text!) {
let combinednumber = "\(possibleInt * 25)"
lblCalcedNumber.text = combinednumber
}
else {
txtBox.text = ""
txtBox.placeholder = "Please Enter a Valid Number"
}
}
Your variables 'number1' and 'possibleInt' have the same value, so you only need one of them for this section of code. Since 'number' is only used once it would be better to use the value itself rather than create a variable for it, however, if you use it elsewhere keep it as a variable so you only need to change your code in one spot. If you weren't using the possibleInt/number1 value inside the if statement, you could be even more efficient by doing if Int(txtBox.text!) != nil. Try this:
// Mark: Actions
#IBAction func btnCalc(sender: UIButton) {
let possibleInt = Int(txtBox.text!)
if possibleInt != nil {
let combinednumber = "\(possibleInt * 25)"
lblCalcedNumber.text = combinednumber
}
else {
txtBox.text = ""
txtBox.placeholder = "Please Enter a Valid Number"
}
}

Closure argument becomes nil

I have a weird issue trying to validate user input. I'm using a wrapper for a form framework and I want it to be able to validate user input.
The trouble is, when I call the closure with the userValue argument, it ends up being nil and all checks return false:
class FormRowWrap {
var tag: String
var row: AnyObject
var verification: (value: Any?) -> Bool
init(tag: String, row:AnyObject, verification:(Any?) -> Bool) {
self.tag = tag
self.row = row
self.verification = verification
}
}
class InsertViewController: FormViewController {
let rows = [
{
let tag = "Fuel Name"
let row = FormRowWrap(tag: tag,
row:TextRow(tag) {
$0.title = tag
// $0.value = last known user default
},
verification:({(value: Any?) -> Bool in
if let thing = value as? String {
//^----- the value in a **breakpoint here is nil**
//
if !thing.isEmpty {
return true
}
}
return false
}))
return row
}() as FormRowWrap,
{
let tag = "Price"
let row = FormRowWrap(tag: tag,
...
func formValuesAreValid(values: [String: Any?]) -> Bool {
var result = false
for rowWrap in self.rows {
let userValue = values[rowWrap.tag]
print("userValue \(userValue) forTag: \(values[rowWrap.tag])")
// ^---- this prints userValue **Optional(Optional("Ghh")) forTag: Optional(Optional("Ghh"))**
let entryIsValid = rowWrap.verification(value: userValue)
if (!entryIsValid) {
result = false
return result
}
}
result = true
return result
}
If I run rowWrap.verification(value:"test") it returns true, so I think it's an issue about properly unwrapping values.
Your function needs an Optional but userValue is an Optional inside another Optional:
Optional(Optional("Ghh"))
So when you force unwrap it with
let entryIsValid = rowWrap.verification(value: userValue!)
what happens actually is that you unwrap the first layer and get back an Optional:
Optional("Ghh")
which is what your function signature
(value: Any?) -> Bool
needs.
About why it's wrapped twice:
with
formValuesAreValid(values: [String: Any?])
the values in the values dictionary are Optionals, and then when you access the dictionary:
let userValue = values[rowWrap.tag]
you get yet another Optional - because accessing a dictionary always returns an Optional, so in this case values[rowWrap.tag] returns an "Optional Optional".
Then somewhere else you unwrap once thinking you'll get the value if let thing = value as? String but you get the inner Optional instead and your next check fails.
Found a way to make it work by force unwrapping the "userValue":
let entryIsValid = rowWrap.verification(value: userValue!)
I still don't understand why this works and why it doesn't work with the argument as wrapped optional.

Resources