Deteting item in an array stored in UserDefaults - ios

I am saving items in UserDefaults and it works fine. I simply append new elements to the array. now deleting the entire saved items is done but now I want to enable the user the ability to delete just one item instead of deleting the entire saved items.
below is how I delete all the entire array
public func deleteSavePropery() {
delete(key: propertyKey)
}
private func delete(key: String) {
storage.removeObject(forKey: key)
}
NOTE, saveProperty is a Codable object

You need to retrieve the array if exists then delete the item finally save back
let storage = UserDefaults.standard
private func deleteItem(key: String,item:Item) {
if let data = storage.data(forKey: key) , var arr = try? JSONDecoder().decode([Item].self, from: data) {
arr.removeAll(where: { $0 == item})
guard let res = try? JSONEncoder().encode(arr) else { return }
storage.set(res, forKey: key)
}
}
struct Item:Codable,Equatable {
let name:String
}

You need to,
Fetch the array from UserDefaults.
Remove the element from the array based on your condition.
Save the array back to UserDefaults.
Example-1: removing the element from array based on the index.
func remove(at index: Int) {
if let data = storage.data(forKey: "YOUR_KEY") {
do {
var arr = try JSONDecoder().decode([Item].self, from: data)
arr.remove(at: index)
let data = try JSONEncoder().encode(arr)
storage.set(data, forKey: "YOUR_KEY")
} catch {
print(error)
}
}
}
Example-2: removing a particular element from array.
func remove(element: Item) {
if let data = storage.data(forKey: "YOUR_KEY") {
do {
var arr = try JSONDecoder().decode([Item].self, from: data)
arr.removeAll { $0 === element }
let data = try JSONEncoder().encode(arr)
storage.set(data, forKey: "YOUR_KEY")
} catch {
print(error)
}
}
}

UserDefaults doesn't provide any API to manage the contents of a particular key. In fact, it doesn't care what kind of content is saved there.
So, what you need to do is: extract the array from UserDefaults, edit it and then save an updated array to UserDefaults.

Related

Realm List not saving data

I am fetching data from FireBase and save it inside my realm but it is not working as expected :
for doc in docs {
let shopData = doc.data()
let newShop = RLMShop()
newShop.shopName = shopData["name"] as? String ?? "Empty Name"
self.saveShop(shop: newShop) // My issue is here
}
My saveShop function :
func saveShop(shop: RLMShop) {
do {
try realm.write {
realm.add(shop)
}
} catch {
print("Error saving shop \(error)")
}
}
Calling save function is not saving my object.
The problem you have is that you are creating a RLMShop object but it is not linked to a RLMShopsCategory object, therefore your shopsList will not contain the new object.
// Fetch the RLMShopsCategory that you wish to add the RLMShop too
// Using Primary Key here just as an example
let shopsCategory = realm.object(ofType: RLMShopsCategory.self, forPrimaryKey: "YourKey")
for doc in docs {
let shopData = doc.data()
let newShop = RLMShop()
newShop.shopName = // setting the properties etc
// This is the part you are missing
// You need to append the newShop to your shopsCategory object
try! realm.write {
shopsCategory.shopsList.append(newShop)
}
}

How to access & get nested values from IOS Swift 'Any' type?

I am trying to read from Firestore into a Dictionary[Any] type using Struct. I can get the values loaded into variable "data" dictionary with Any type.
However I cannot loop thru it to access normal nested Dictionary variable.
I cannot get Key, values printed.
Following is my code:
class PullQuestions {
//shared instance variable
**public var data = [Any]()**
private var qdb = Firestore.firestore()
public struct questionid
{
let qid : String
var questions : [basequestion]
var answers: [baseans]
}
public struct basequestion {
let category : String
let question : String
}
public struct baseans {
let answer : String
}
class var sharedManager: PullQuestions {
struct Static {
static let instance = PullQuestions()
}
return Static.instance
}
static func getData(completion: #escaping (_ result: [Any]) -> Void) {
let rootCollection = PullQuestions.sharedManager.qdb.collection("questions")
//var data = [Any]()
rootCollection.order(by: "upvote", descending: false).getDocuments(completion: {
(querySnapshot, error) in
if error != nil {
print("Error when getting data \(String(describing: error?.localizedDescription))")
} else {
guard let topSnapshot = querySnapshot?.documents else { return }
// var questiondoc = [basequestion]()
for questioncollection in topSnapshot {
rootCollection.document(questioncollection.documentID).collection("answers").getDocuments(completion: {
(snapshot, err) in
guard let snapshot = snapshot?.documents else { return }
var answers = [baseans]()
for document in snapshot { //There should be only one Document for each answer collection
//Read thru all fields
for i in 0..<document.data().count
{
let newAns = baseans(answer: answer)
print("Answer Docs=>", (answer))
answers.append(newAns)
}
}
let qid = questioncollection.documentID
let category = questioncollection.data()["category"] as! String
let question = questioncollection.data()["question"] as! String
let newQuestions = basequestion(category: category ,question: question)
let newQuestionDict = questionid(qid: qid, questions: [newQuestions], answers: answers)
PullQuestions.sharedManager.data.append(newQuestionDict)
//Return data on completion
completion(PullQuestions.sharedManager.data)
})
}
}
})
}
}
I can print like this
print("Count =>", (PullQuestions.sharedManager.data.count))
// print(PullQuestions.sharedManager.data.first ?? "Nil")
print(PullQuestions.sharedManager.data[0])
for element in PullQuestions.sharedManager.data
{
print("Elements in data:=>", (element))
}
I could access only the key.. how do i go and get the nested values ?
First of all, consider using Swift code conventions (e.g. your structs are named with small letters, but you should start with capital), this will make your code more readable.
Returning to your question. You use an array instead of dictionary (this piece of code: public var data = [Any]()). And here you are trying to print values:
for element in PullQuestions.sharedManager.data
{
print("Elements in data:=>", (element))
}
In this context element is an Any object, thus you cannot access any underlying properties. In order to do this you have two options:
1. You should specify the type of array's objects in it's declaration like this:
public var data = [questionid]()
or you can user this:
public var data: [questionid] = []
These two are equals, use the one you prefer.
2. If for any reasons you don't want to specify the type in declaration, you can cast it in your loop. Like this:
for element in PullQuestions.sharedManager.data
{
if let element = element as? quetionid {
print("Elements in data:=>", (element))
// you can also print element.qid, element.questions, element.answers
} else {
print("Element is not questionid")
}
}
You could of course use the force cast:
let element = element as! questionid
and avoid if let syntax (or guard let if you prefer), but I wouldn't recommend this, because it (potentially) can crash your app if element will be nil or any other type.

Removing Custom Objects from List in UserDefaults

I am trying to remove an element from a list which is stored in NSUserDefaults. The getAll function is implemented below:
func getAllOrders() -> [Order] {
var orders = [Order]()
if let userDefaults = UserDefaults(suiteName: "group.com.johndoe.SoupChef.Shared") {
if let ordersData = userDefaults.data(forKey: "Orders") {
orders = try! JSONDecoder().decode([Order].self, from: ordersData)
}
}
return orders
}
And here is the code for deleting the order.
func delete(order :Order) {
var persistedOrders = getAllOrders()
persistedOrders.removeAll { persistedOrder in
persistedOrder.identifier.uuidString == order.identifier.uuidString
}
}
After deleting the order in the code above when I call getAllOrders I still see all the elements, meaning I don't see the order being deleted.
That's because you don't save your changes. Once you've performed the removal you need to turn persistedOrders back into JSON and then:
userDefaults.set(json, forKey:"Orders")
You need to use jsonEncoder and encode the edited array then store it again the user defaults
func delete(order :Order) {
var persistedOrders = getAllOrders()
persistedOrders.removeAll { persistedOrder in
persistedOrder.identifier.uuidString == order.identifier.uuidString
}
do {
let data = try JSONEncoder().encode(persistedOrders)
userDefaults.set(data, forKey:"Orders")
}
catch {
print(error)
}
}
You have to store correctly your UserDefaults
UserDefaults.standard.set(json, forKey:"Orders")
Now, you can remove them using:
UserDefaults.standard.removeObject(forKey: "Orders")

TableView reloading too early

I'm running into a weird issue where my tableView is reloading too early after retrieving JSON data. The strange thing is sometimes it reloads after getting all the required data to fill the tableView and other times it reloads before it can acquire the data. I'm not entirely sure why it's doing this although I do notice sometimes the data is returned as nil. Here is what I use to retrieve the data:
var genreDataArray: [GenreData] = []
var posterStringArray: [String] = []
var posterImageArray: [UIImage] = []
override func viewDidLoad() {
super.viewDidLoad()
GenreData.updateAllData(urlExtension:"list", completionHandler: { results in
guard let results = results else {
print("There was an error retrieving genre data")
return
}
self.genreDataArray = results
for movie in self.genreDataArray {
if let movieGenreID = movie.id
{
GenrePosters.updateGenrePoster(genreID: movieGenreID, urlExtension: "movies", completionHandler: {posters in
guard let posters = posters else {
print("There was an error retrieving poster data")
return
}
for poster in posters {
if let newPoster = poster {
if self.posterStringArray.contains(newPoster){
continue
} else {
self.posterStringArray.append(newPoster)
self.networkManager.downloadImage(imageExtension: "\(newPoster)",
{ (imageData)
in
if let image = UIImage(data: imageData as Data){
self.posterImageArray.append(image)
}
})
break// Use to exit out of array after appending the corresponding poster string
}
} else {
print("There was a problem retrieving poster images")//This gets called sometimes if the poster returns nil
continue
}
}
})
}
}
DispatchQueue.main.async {
self.genresTableView.reloadData()//This is reloading too early before the data can be retrieved
}
})
}
The data is being retrieved asynchronously, and thus your table view can sometimes reload without all the data. What you can do is have the table view reload at the end of the asynchronous data retrieval, or you can reload the cells individually as they come in instead of the whole table using
let indexPath = IndexPath(item: rowNumber, section: 0)
tableView.reloadRows(at: [indexPath], with: .top)
TRY THIS-:
var genreDataArray: [GenreData] = []
var posterStringArray: [String] = []
var posterImageArray: [UIImage] = []
override func viewDidLoad() {
super.viewDidLoad()
genredataArray.removeAll()
posterStringArray.removeAll()
posterImageArray.removeAll()
NOW HERE CALL YOUR CLASS FUNCTION AS ABOVE
}
I guess in that case, you should use
dispatch_async(dispatch_get_main_queue(),{
for data in json as! [Dictionary<String,AnyObject>]
{
//take data from json. . .
}
//reload your table -> tableView.reloadData()
})
You should get the main queue of the thread.

Removing from array of dictionary

I use UserDefualt to save array of Dictionary:
let pref = NSUserDefaults.standardUserDefaults();
var fav=pref.arrayForKey("favRecipes")!;
fav.append(self.dict);
pref.setObject(fav, forKey: "favRecipes");
before i save another data i check if it already exist:
#IBAction func addToFavorites(sender: UIButton) {
//not exist
if !isFavorite(){
var fav=pref.arrayForKey("favRecipes")!;
fav.append(self.dict);
pref.setObject(fav, forKey: "favRecipes");
print("added");
} //exist
else{
;
}
}
private func isFavorite()->Bool{
var fav=pref.arrayForKey("favRecipes")!;
for r in fav{
if r["id"]! as! Int==id{
return true;
}
}
return false;
}
The purpose of this code is to add something to your favorites list, and if its already there u have the option to remove it.
for some reason i'm not succeeding to do the last part.
this my try so far:
func remove(event:UIAlertAction!){
var fav=pref.arrayForKey("favRecipes")!;
for r in fav{
if r["id"]! as! Int==id{
pref.removeObjectForKey("\(id)");
//pref.synchronize();
//fav=pref.arrayForKey("favRecipes")!;
//print(r["id"] as! Int);
}
}
}
ofcourse i tried to look and search about removing from dictionarys at userDefualt but nothing helps.
You have to change the array fav and write that array back into the userDefaults. There is no object with the key \(id) in the defaults, only an array for key favRecipes which in turn contains an entry for the key id.
func remove(event:UIAlertAction!){
var fav = pref.arrayForKey("favRecipes")!
fav = fav.filter { $0["id"] != id }
pref.setObject(fav, forKey: "favRecipes")
}

Resources