Swift 5 Clear NSKeyedArchiver.archivedData by key from UserDefaults - swift

i have method (provided below) that helps me to save [UIImage] to userDefault. It works fine but i need to clear that data before insert new array.
This way, doesn't work.. .removeObject(forKey: ), it remove only last item from array. Also i tried to set empty array [ ] or nil.
func set(images value: [UIImage]?, forKey defaultName: String) throws {
guard let value = value else { removeObject(forKey: defaultName); return }
try set(NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: false), forKey: defaultName)
}

Related

How to filter an array of dictionaries in Swift

I have chosen to populate an NSTableview by using a mutable array of dictionary objects where the dictionary key is the column identifier and the value is the column value. I populate the array like this:
var compArray: NSMutableArray = []
let dict = ["idCompany": id, "company": companyName, "compType":
companyType] as [String : Any]
compArray.add(dict)
id, company and compType come from an SQlite query.
I use compArray as the tableView data source. This works great. There is no array controller involved.
Table is loaded as follows using CDM which is an instance of the class that delivers the compArray
//Define the function that will get the data for each cell for each
//row.
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
//Define constant dict as an NSDictionary and set to the DM
//instance of the DataModel class at the row being loaded into the
//table. DM needs to be cast as an NSDictionary. row is passed in as a
//parameter by the OS
let tdict:NSDictionary = CDM.compArray[row] as! NSDictionary
//Define strKey as the column identifier for the column being
//loaded. Column being loaded is passed in as a parameter by the OS
let strKey = (tableColumn?.identifier)!
//method will return the value from dict (which is loaded from
//CDM.compArray) for the key that is equal to the column identifier
//which was loaded to strKey
return tdict.value(forKey: strKey.rawValue)
}
What I would like to do is introduce an NSSearch field to search all the columns of the tableview.
I added the searchfield as an action and I also added code to store compArray into a copy called backupCompArray.
I defined an isSearching variable:
//The variable is set to true when searching
var isSearching = false {
//This will be fired if the state of isSearching changes ie false
//to true or true to false
didSet {
//Test to see whether isSearching now has a new value and if
//so we do the housekeeping
if isSearching != oldValue {
//If isSearching is now true then it must have previously
//been false and we now need to back up the original array
if isSearching {
//Back up the original array
backUpCompArray = compArray
} else{
//It is now turning from true to false so need to
//restore
compArray = backUpCompArray
}
}
}
}
I want to be able to filter compArray based on the .stringValue of the searchfield.
I have put the following in the #IBAction of the searchfield:
#IBAction func searchField(_ sender: NSSearchFieldCell) {
if sender.stringValue.isEmpty {
//If stringValue is empty then cant be searching and this can
//trigger the restore of the original array
isSearching = false
} else {
//If stringValue is not empty then must be searching. When the
//search starts need to backup the original array
isSearching = true
//?????????????
}
}
tableView.reloadData()
}
I need to replace the ? with code that can set compArray by applying a filter to backupCompArray returning any rows to compArray where the dictionary values for keys "company" and "compType" contains the searchfield.Stringvalue. I can then use the modified compArray to load the table with only filtered rows.
So I tried your code and got two errors that I tried to fix as follows:
//Can use the .filter method it will iterate each value in the array
CDM.compArray = backupCompArray.filter(using: {
// this is where you determine whether to include the
specific element, $0
$0["company"]!.contains(sender.stringValue) &&
$0["compType"]!.contains(sender.stringValue)
// or whatever search method you're using instead
})
}
That is inserting 'using' and changing searchString to sender.Stringvalue.
But I now get:
against the line that ends with &&
You can do this with Swift 4's Array.filter(). It takes a function where you can do your computation. You actually shouldn't need to back up your compArray, unless you need to keep it around later for something else.
compArray = compArray.filter({
// this is where you determine whether to include the specific element, $0
$0["company"]!.contains(searchString) &&
$0["compType"]!.contains(searchString)
// or whatever search method you're using instead
})

Force Unwrap Swift Int Row Value

I am trying to get the value from a row, turn it into a string and then check to see if that string is in an array. So far I have this:
let row: IntRow? = form.rowBy(tag: "devicefield")
let stringvalue = String(describing: row?.value)
if (row?.value) != nil
{
if tableDevices.contains("\(stringvalue)")
{
self.dismiss(animated: false, completion: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "filterData"), object: nil)
}
else
{
SCLAlertView().showError("Error", subTitle: "\(stringvalue) does not exist at your facility")
return
}
}
But for some reason it keeps printing out my variable stringvalue as an optional.
row?.value is an optional. That's pretty clear since you see the "?". So the print command you are using is simply printing out that it is an optional. The string isn't optional, the variable is. You can get a new variable like this:
if let printableRowNumberAsInt = row?.value {
// do your stuff like...
print("\(printableRowNumberAsInt)")
}

Guard let construction, still getting fatal error: Index out of range

I am trying to parse some data with this code:
func findDate(data: String?) -> String {
guard let date: String? = (data!.componentsSeparatedByString("T"))[0] else{
return "20000101"
}
return date!
}
I tried the guard structure to prevent errors when there is no data found, or it has a different structure, but I still get the error when I run it:
fatal error: Index out of range
Does somebody know how to fix this?
The access of the element at index zero always happens. If the result of the call to components(separatedBy:) returns an empty array, your code crashes. Also, you should avoid force unwrapping data.
A solution to these crashes is to use the first property of the array, which is optional, so you can safely unwrap it.
guard let date = data?.components(separatedBy: "T").first else {
return "20000101"
}
return date.
Also, this could then be simplified using the nil coalescing operator:
return data?.components(separatedBy: "T").first ?? "20000101"
Please check out this code:
func findDate(data: String?) -> String {
guard let date: String? = (data?.componentsSeparatedByString("T"))?[0] else{
return "20000101"
}
return date!
}
findDate(nil)
findDate("")
findDate("98588T99")
There are two issues here:
1) Trying to access an index of an array that isn't large enough doesn't produce nil, it just crashes. For example, this code will crash:
let strings: [String] = []
guard let firstString = strings[0] else {
// couldn't find firstString
}
Instead, use the first method:
let strings: [String] = []
guard let firstString = strings.first else {
// couldn't find firstString
}
first attempts to access the first element of the array, and it returns nil if the array doesn't have a first element.
2) The purpose of guard let is to guarantee that the value you get out isn't nil. So your date value should be of type String, not String?, and you shouldn't have to force-unwrap it. For example:
let strings: [String] = ["one", "two", "three"]
guard let firstString = strings.first else {
// no first string
}
print(firstString) // firstString is not nil, no need to unwrap it

Issues adding NSIndexPath as key and NSNumber as value in Dictionary?

Second object of quickFilterDictionary in self.preferencesListFilterDictionary is nil. How to add [NSIndexPath] index path (as key) and NSNumber (as value) in Dictionary?
Note: first object is added successfully, only the 2nd object is nil when I printed self.preferencesListFilterDictionary in console. May I know , what am I doing wrong in below code?
self.preferencesListFilterDictionary["price"] = 1000
self.quickFilterArr = [localizedStrOf("deliveryNowOnly"),localizedStrOf("rated")]
var quickFilterDictionary = Dictionary<NSIndexPath, NSNumber?>()
for obj in self.quickFilterArr {
let row = self.quickFilterArr.indexOf(obj)
let indexPath = NSIndexPath(forRow:row!, inSection: 0)
quickFilterDictionary[indexPath] = NSNumber(bool: false)
}
print(quickFilterArr.count)
print(quickFilterDictionary)
self.preferencesListFilterDictionary.updateValue(quickFilterDictionary as? AnyObject, forKey: "quickFilterDictionaryKey")
print(self.preferencesListFilterDictionary)
console print:
▿ [1] : 2 elements
- .0 : "quickFilterDictionaryKey"
- .1 : nil
To get above dictionary bool from number, I wrote following code -
func getQuickFilterValueOfIndexpath(indexPath:NSIndexPath) -> Bool {
if let val = self.preferencesListFilterDictionary["quickFilterDictionaryKey"] {
// now val is not nil and the Optional has been unwrapped, so use it
let tempDict = val as! Dictionary<NSIndexPath, NSNumber?>
if let boolVal = tempDict[indexPath] {
return boolVal!
}
}
return false
}
I think you are confused on the difference between a Row, an IndexPath, and an Index.
In the following code:
let row = self.quickFilterArr.indexOf(obj)
If you option click on row, you will see that it is an object of type Array.Index?. It is not an row.
Also is there a reason you are using NSNumber, NSIndexPath and update value for key? As opposed to Number, IndexPath and just updating your dictionary values with:
preferencesListFilterDictionary["quickFilterDictionaryKey] = quickFilterDictionary
If you option click on quickFilterArr and it is an array of IndexPaths [IndexPath] - To reference the IndexPath at your chosen index in that array you would just do:
if let arrayIndex = quickFilterArr.indexOf(obj) {
//if your array is an array of Index Paths
//you would then pull the IndexPath object at the index you have defined above
let indexPath = quickFilterArr[arrayIndex]
quickFilterDictionary[indexPath] = Number(bool: false)
}

Determining if Swift dictionary contains key and obtaining any of its values

I am currently using the following (clumsy) pieces of code for determining if a (non-empty) Swift dictionary contains a given key and for obtaining one (any) value from the same dictionary.
How can one put this more elegantly in Swift?
// excerpt from method that determines if dict contains key
if let _ = dict[key] {
return true
}
else {
return false
}
// excerpt from method that obtains first value from dict
for (_, value) in dict {
return value
}
You don't need any special code to do this, because it is what a dictionary already does. When you fetch dict[key] you know whether the dictionary contains the key, because the Optional that you get back is not nil (and it contains the value).
So, if you just want to answer the question whether the dictionary contains the key, ask:
let keyExists = dict[key] != nil
If you want the value and you know the dictionary contains the key, say:
let val = dict[key]!
But if, as usually happens, you don't know it contains the key - you want to fetch it and use it, but only if it exists - then use something like if let:
if let val = dict[key] {
// now val is not nil and the Optional has been unwrapped, so use it
}
Why not simply check for dict.keys.contains(key)?
Checking for dict[key] != nil will not work in cases where the value is nil.
As with a dictionary [String: String?] for example.
The accepted answer let keyExists = dict[key] != nil will not work if the Dictionary contains the key but has a value of nil.
If you want to be sure the Dictionary does not contain the key at all use this (tested in Swift 4).
if dict.keys.contains(key) {
// contains key
} else {
// does not contain key
}
Looks like you got what you need from #matt, but if you want a quick way to get a value for a key, or just the first value if that key doesn’t exist:
extension Dictionary {
func keyedOrFirstValue(key: Key) -> Value? {
// if key not found, replace the nil with
// the first element of the values collection
return self[key] ?? first(self.values)
// note, this is still an optional (because the
// dictionary could be empty)
}
}
let d = ["one":"red", "two":"blue"]
d.keyedOrFirstValue("one") // {Some "red"}
d.keyedOrFirstValue("two") // {Some "blue"}
d.keyedOrFirstValue("three") // {Some "red”}
Note, no guarantees what you'll actually get as the first value, it just happens in this case to return “red”.
if dictionayTemp["quantity"] != nil
{
//write your code
}
My solution for a cache implementation that stores optional NSAttributedString:
public static var attributedMessageTextCache = [String: NSAttributedString?]()
if attributedMessageTextCache.index(forKey: "key") != nil
{
if let attributedMessageText = TextChatCache.attributedMessageTextCache["key"]
{
return attributedMessageText
}
return nil
}
TextChatCache.attributedMessageTextCache["key"] = .some(.none)
return nil
Here is what works for me on Swift 3
let _ = (dict[key].map { $0 as? String } ?? "")

Resources