I am tried to add more than one array but it delete old and create new one. How can I add more / insert another array to UserDefaults? Here my code:
let foodArray = ["Title": String(fShop_TitleInput.text ?? ""), "QTY": String(fShop_QTYInput.text ?? ""), "Price": String(fShop_PriceInput.text ?? "")]
fShop_UserDefault.set(foodArray, forKey: "FoodArraySave")
let getFoodArray = fShop_UserDefault.object(forKey: "FoodArraySave")
print(getFoodArray ?? "No Array")
This work fine, but will save only one array at time. Example, I create new array title will be TestOne. If I add new title will be TestTwo, the array erase TestOne. How can I solve it?
As matt points out, your data is of type Dictionary, aka [String: Any]. Whenever you save it to UserDefaults, it gets overwritten, because you're writing to the same key. If you want to add more data to the same key, you'll have to convert your dictionary to an array of dictionaries: [[String: Any]]. Then you can access individual dictionaries by enumeration, or by accessing the particular index you're interested in.
The other way would be to save each piece of data in a different key, like "FoodArraySave1", "FoodArraySave2", "FoodArraySave3". That's probably more involved and less efficient though.
This is probably what you want to do:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var myFoodData: [[String: Any]] = []
let foodArray: [String: Any] = ["Title": "Eggs", "QTY": 18, "Price": 5.50]
let foodArray2: [String: Any] = ["Title": "Bacon", "QTY": 12, "Price": 12.50]
myFoodData.append(foodArray)
UserDefaults.standard.set(myFoodData, forKey: "FoodArraySave")
var getFoodData = UserDefaults.standard.object(forKey: "FoodArraySave") as! [[String: Any]]
print(getFoodData[0])
myFoodData.append((foodArray2))
UserDefaults.standard.set(myFoodData, forKey: "FoodArraySave")
getFoodData = UserDefaults.standard.object(forKey: "FoodArraySave") as! [[String: Any]]
print(getFoodData[1])
}
Related
I'm kind of new to Swift currently playing around with Stickers.
I have a JSON file with the following structure:
{
"stickers": [{
"filename": "happy_face",
"description": "Happy Face",
"premium": "false",
"categories": ["blue", "green", "red"],
"genders": ["male", "female"]
},{
"filename": "sad_face",
"description": "Sad Face",
"premium": "false",
"categories": ["blue", "green", "red", "yellow"],
"genders": ["male"]
}]
}
Stickers will have the same filename, but will be separated into folders according to their category and gender.
I can read the JSON data just fine. My problem is when I'm trying to make some use of the JSON data.
My goal is to separate the stickers according to their categories, which could vary depending on the sticker, the user will later be able to switch categories, and the correct stickers will be displayed.
let stickerPack = StickerPack.load()
let allStickers = stickerPack!["stickers"] as? [[AnyHashable : Any]]
func getStickersWithCategory(category: String){
var stickers = [AnyObject]()
for sticker in allStickers! {
let cat = sticker["categories"] as? [String]
for item in cat! {
if item.contains(category){
stickers.append(sticker)
}
}
}
}
The result of this is
"Argument type '[AnyHashable : Any]' does not conform to expected type 'AnyObject'"
Can anyone point me in the right direction? Is it possible with this JSON structure? or is it better to have a different structure, with each category and gender separated? this would lead to a lot of repetition. But maybe I'm creating more problems by trying to keep the JSON structure this way.
All help appreciated!
Be more type specific, this avoids that kind of errors.
This JSON array is [[String:Any]] and never AnyHashable because the keys are required to be string
let allStickers = stickerPack!["stickers"] as? [[String:Any]]
stickers is the same type and never something including AnyObject. All JSON types are value types (Any).
var stickers = [[String:Any]]()
You can filter the array swiftier and it's recommended to safely unwrap all optionals
if let stickerPack = StickerPack.load(),
let allStickers = stickerPack["stickers"] as? [[String:Any]] {
stickers = allStickers.filter({ (sticker) -> Bool in
guard let categories = sticker["categories"] as? [String] else { return false }
return categories.contains(category)
})
...
The issue is that you declared allStickers as let allStickers = stickerPack!["stickers"] as? [[AnyHashable : Any]], which means that its type will be [[AnyHashable:Any]]? or Array<Dictionary<AnyHashable:Any>>. When you are iterating through that array in for sticker in allStickers!, the type of sticker will be Dictionary<AnyHashable:Any>. Dictionary is a struct, so it doesn't conform to AnyObject, and hence you cannot add sticker to stickers, which is declared as an array of AnyObjects.
So changing var stickers = [AnyObject]() to var stickers = [Any](), or more specifically var stickers = [[AnyHashable:Any]]() should solve your issue.
func getStickersWithCategory(category: String){
var stickers = [[AnyHashable:Any]]()
for sticker in allStickers! {
let cat = sticker["categories"] as? [String]
for item in cat! {
if item.contains(category){
stickers.append(sticker)
}
}
}
}
Btw you are encouraged to use Codable in Swift 4 when handling JSON.
I have UICollectionView contains: UIlabel and UIButton (text in labels comes from API)
when I press UIButton text from UIlabel and CostLbl stored in UserDefaults.standard.array(forKey: "car")
let imageData = UIImageJPEGRepresentation(image.image!, 1.0) as NSData?
let newValues = ["name": self.nameLbl.text ?? "", "price": self.costLbl.text ?? "", "image": imageData]
var mArray = UserDefaults.standard.array(forKey: "car") as? [[String: Any]]
maAr?.append(newValues)
Question : when UIButton is press need to check if text from UIlabel is in array(forKey: "car") , if not - can to stored for key,, and if text already in array(forKey: "car") cant add to array
I wrote as vadian suggested
var maAr = UserDefaults.standard.array(forKey: "car") as? [[String: Any]] ?? []
if !maAr.contains(where: newValues) {
maAr.append(newValues)
def.set(maAr, forKey: "car")
}
but catch error to (where: newValues) -
Cannot convert value of type '[String : Any]' to expected argument type '([String : Any]) throws -> Bool'
How can this be changed?
First of all declare the dictionary as [String:String] in this case there is no type cast and no annotation.
let newValues = ["name": nameLbl.text ?? "", "price": costLbl.text ?? ""]
Read the array, check if the array contains the value, if NO append the item and save the array back, if YES do nothing
var carArray = UserDefaults.standard.array(forKey: "car") as? [[String: String]] ?? []
if !carArray.contains(newValues) {
carArray.append(newValues)
UserDefaults.standard.set(carArray, forKey: "car")
}
I have array NSUserDefaults type :
let userValue = ["name": self.nameLbl.text ?? "", "lastName": self.lastNameLbl.text ?? "", "city": cityLbl.text ?? ""] as! [String : String]
var userArray = UserDefaults.standard.array(forKey: "userInfo") as? [[String: String]] ?? []
UserDefaults.standard.set(userArray, forKey: "userInfo")
And save this array in first `ViewController`
In second ViewController Im display this array in UITAbleView
class ...
var userInfo = [[String:String]]()
override func viewDidLoad() {
super.viewDidLoad()
userInfo = UserDefaults.standard.array(forKey: "userInfo") as? [[String: String]] ?? []
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserTableViewCell
var userItem = userInfo[indexPath.row]
cell.nameLbl?.text = item["name"]
cell.lastNameLbl.text = item["lastName"]
cell.cityLbl.text = item["city"]
and under UITAbleView I have 3 UIextField's and UIButton for the opportunity to change user information
When user pass text in UIextField's I want to resave value for key
How can I remove selected name and save new name to correctly user??
To Abhirajsinh Thakore answer :
Im update code in cellForRowAt
var userItem = userInfo[indexPath.row]
cell.nameLbl?.text = item["name"]
cell.lastNameLbl.text = item["lastName"]
cell.cityLbl.text = item["city"]
item["name"] = cell.nameLbl?.text
var maArray = UserDefaults.standard.array(forKey: "userInfo") as? [[String: String]] ?? []
item.updateValue(cell.nameLbl?.text, forKey: "name")
maArray.append(item)
But its not save
You can fetch your complete array from Userdefaults
Now do is that get the new values from textField and replace that at the specific indexPath.row
For e.g:
var userInfo = [[String:String]]()
let data = userInfo[indexPath.row]
data["name"] = txtName.text
data["address"] = txtAddress.text
And now store this full array back to userDefaults and you will get the updated Result every Time.
Hope this helps.
Edit with reference to the code
Do like this:
var maArray = UserDefaults.standard.array(forKey: "userInfo") as? [[String: String]] ?? []
let innerData = maArray[indexPath.row]
innerData["name"] = cell.nameLbl?.text
innerData["lastName"] = cell.lastNameLbl.text
innerData["city"] = cell.cityLbl.text
// And save the maArray back to userDefaults
I'm having an issue saving and retrieving an array in UserDefaults from UIImagePickerControllerImageURL. I can get the array after synchronizing, but I am unable to retrieve it. myArray is empty.
The testImage.image does get the image, no problems there.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let imageURL: URL = info[UIImagePickerControllerImageURL] as! URL
//test that imagepicker is actually getting the image
let imageData: NSData = try! NSData(contentsOf: imageURL)
let cvImage = UIImage(data:imageData as Data)
testImage.image = cvImage
//Save array to UserDefaults and add picked image url to the array
let usD = UserDefaults.standard
var array: NSMutableArray = []
usD.set(array, forKey: "WeatherArray")
array.add(imageURL)
usD.synchronize()
print ("array is \(array)")
let myArray = usD.stringArray(forKey:"WeatherArray") ?? [String]()
print ("myArray is \(myArray)")
picker.dismiss(animated: true, completion: nil)
}
There are many issue here.
Do not use NSData, use Data.
Do not use NSMutableArray, use a Swift array.
You can get the UIImage directly from the info dictionary`.
You can't store URLs in UserDefaults.
You save array to UserDefaults before you update the array with the new URL.
You create a new array instead of getting the current array from UserDefaults.
You needlessly call synchronize.
You needlessly specify the type for most of your variables.
Here is your code updated to fix all of these issues:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
testImage.image = image
}
if let imageURL = info[UIImagePickerControllerImageURL] as? URL {
//Save array to UserDefaults and add picked image url to the array
let usD = UserDefaults.standard
var urls = usD.stringArray(forKey: "WeatherArray") ?? []
urls.append(imageURL.absoluteString)
usD.set(urls, forKey: "WeatherArray")
}
picker.dismiss(animated: true, completion: nil)
}
Note that this saves an array of strings representing each URL. Later on, when you access these strings, if you want a URL, you need to use URL(string: arrayElement).
Trying to store an array of dictionaries with NSUserDefaults.
var theTasks: [[String:Any]] = [["num":1,"title":"example","colour":"red"]]
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(theTasks, forKey: "myTasks")
defaults.synchronize()
let selected = theTasks[1]
Gives error:
Cannot convert value of type '[[String:Any]]' to expected argument of type 'AnyObject?'
Swift 3.x
In Swift 3 it has changed so now it needs to be saved as [Any] Any Array and use UserDefaults array(forKey:) method to load it:
let theTasks: [Any] = [["num": 1, "title": "example", "colour": "red"]]
UserDefaults.standard.set(theTasks, forKey: "myTasks")
if let loadedTasks = UserDefaults.standard.array(forKey: "myTasks") as? [[String: Any]] {
print(loadedTasks)
}
var theTasks: [[String: Any]] {
get {
return UserDefaults.standard.array(forKey: "myTasks") as? [[String: Any]] ?? []
}
set {
UserDefaults.standard.set(newValue as [Any], forKey: "myTasks")
}
}
Swift 2.x
You just need to save it as a AnyObject array and use NSUserDefaults method arrayForKey to load it:
let theTasks: [AnyObject] = [["num": 1, "title": "example", "colour": "red"]]
NSUserDefaults.standardUserDefaults().setObject(theTasks, forKey: "myTasks")
if let loadedTasks = NSUserDefaults.standardUserDefaults().arrayForKey("myTasks") as? [[String: AnyObject]] {
print(loadedTasks)
}
You can also create a computed property with a getter and a setter to do all the work behind the scenes for you as follow:
var theTasks: [[String: AnyObject]] {
get {
return NSUserDefaults.standardUserDefaults().arrayForKey("myTasks") as? [[String: AnyObject]] ?? []
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue as [AnyObject], forKey: "myTasks")
}
}
print(theTasks) // [["title": example, "colour": red, "num": 1]]
theTasks[0]["title"] = "another example"
print(theTasks) // [["title": another example, "colour": red, "num": 1]]
Just call the .setObject() method directly from NSUserDefaults()and it should work fine.
NSUserDefaults().setObject(theTasks, forKey: "myTasks")