Store / Save a #State Array (UserDefaults) - arrays

#State var cardData: [Card] = [
Card(number: 1 ,start: "05.06.2020", end: "15.06.2020", days: 10, success: true)
]
I have cardData, and the user can add more Elements to this array. But I want that it will be saved. Like UserDefaults. Is it possible to Store this (cardData) to UserDefaults?
If not, how can I save it permanent?
Thank you so much!

there are multiple ways. on would be adding a .onChange modifier to your view (available on Xcode 12, not sure about 11):
.onChange(of: cardData) { newValue in
UserDefaults.standard.setValue(newValue, forKey: "Your_UserDefaults_Key")
}
I noticed you are using a custom type that you've made. in that case you first need to convert all your data to some Type that can be saved to UserDefaults (like string, using codable, see: https://www.hackingwithswift.com/example-code/system/how-to-load-and-save-a-struct-in-userdefaults-using-codable)
As someone mentioned, this might not be the best way of saving your data on a persistent storage. If you plan to save a lot of data, and you don't need all that data at all times (for example, most of the times you only need one of those all cards) you should not use UserDefaults, and use CoreData instead. Using UserDefaults is not efficient in the case i described, because you have to get all the data from the device's storage everytime (you can't ask to only get the one card you need using UserDefaults, but you can do that using CoreData). And also when saving data to the device's storage, you have to replace the whole old data with some new data (in CoreData you can simply just add one more card. you won't need to replace (all-current-cards) with (all-current-cards + 1-new-card) )

Related

Error when trying to set array in userdefaults: Thread 1: "Attempt to insert non-property list object

I have solved the issue now, thanks for your help. I shouldn't have tried to save arrays with UITextViews, but I should have saved their text as strings instead. Here was the original question:
I have tried a lot, and googled a lot, but I can't solve this problem on my own. Whenever I try to save an array in userdefaults, it just is not working. I get the following error:
Thread 1: "Attempt to insert non-property list object (\n "<UITextView: 0x14001f800; frame = (0 0; 355 180); text = 'D'; clipsToBounds = YES; gestureRecognizers = <NSArray: 0x600003f01d10>; layer = <CALayer: 0x6000031c83e0>; contentOffset: {0, 0}; contentSize: {355, 30}; adjustedContentInset: {0, 0, 0, 0}>"\n) for key content"
I don't know what a non-property list object is. And I do not know how to solve the problem. Below is the lines of code that do not work.
var contentList: [Any] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
contentList.append(label)
defaults.setValue(contentList, forKey: "content")
If I take out the last line of code by turning it into a comment everything runs just fine. How should I replace that line of code? I essentially want to save an array of UITextViews and make it larger every time I call a fucntion (this code is part of a larger function). The reason why I have created another two lists (cl and contentList) is that it helps me with a problem down the line. What I cannot understand however, is why the last line of code doesn't work. If anyone has any ideas, please help me, it would be much appreciated.
Use only String as stated in comments :
var contentList: [String] = []
let cl = defaults.array(forKey: "content")!
if cl.count != 0{
contentList += cl
}
If lbText = label.text {
contentList.append(lbText)
defaults.setValue(contentList, forKey: "content")
}
You can only store a very limited list of data types into UserDefaults, commonly referred to as "property list objects" (Since property list (or plist) files will only store the same data types.
To quote the Xcode docs on UserDefaults, in the section titled "Storing Default Objects":
A default object must be a property list—that is, an instance of (or for collections, a combination of instances of) NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary [or Data, String, NSNumber, Date, Array, or Dictionary types in Swift.] If you want to store any other type of object, you should typically archive it to create an instance of Data.
(I added the equivalent Swift types to the above quote in square brackets, since it looks like Apple hasn't updated it for Swift.)
That's worded a little awkwardly. The idea is that you can only store data of the types listed. Because the Array and Dictionary types are "container" types, you can store any combination of arrays and dictionaries that contain combinations of any of the above types. For example, you can store an array that contains a dictionary, 3 dates, 2 floats, a Double, some Data, and 2 arrays, and those dictionaries and arrays can contain other dictionaries and/or arrays.)
It is almost always wrong to archive UIView objects like UITextViews. You should save the text properties of your text views instead.
If you want to manage a vertical stack of UITextView objects, I suggest adding a vertical stack view to your user interface, and then writing code that adds or removes UITextView subviews to your stack view. You should be able to find plenty of examples of adding and removing objects from stack views online. (It's really easy.)
If you want to manage a scrolling list of feeds of arbitrary length, you might want to use a table view or collection view instead. Those require that you set up a data model and implement a "data source". That takes a little practice to get right, but is very powerful.

Save Images in Room Database

I want to save small images in my room database and I have two issues:
How do I save an image in my database?
How do I save multiple images in my database?
I tried saving a bitmap in the way recommended by the developers page (https://developer.android.com/training/data-storage/room/defining-data)
#Parcelize
#Entity(tableName = "image_table")
data class ImgMod(
#PrimaryKey(autoGenerate = true)
var invoiceId: Long = 0L,
#ColumnInfo(name = "image")
var single_img: Bitmap?
): Parcelable
However, I receive the following error:
Cannot figure out how to save this field into database. You can consider adding a type converter for it.
Secondly, I would like to save multiple images in one database entry. But I receive the same error with the following snippets:
#ColumnInfo(name = "imageList")
var img_list: ArrayList<BitMap>
or decoded bitmap as a String
#ColumnInfo(name = "imageList")
var decoded_img_list: ArrayList<String>
I am sorry if this is a very basic question. But how do I have to configure the database/process the data to an image list?
Thank you in advance,
rot8
A very simple way to get images into the database (although I personally discourage it) would be to base-64 encode the bitmaps into a String and put it into a row of the database.
Take in account that bitmaps are very memory heavy; and base64 encoding something increases it's size some more so be careful when loading a bunch of images... I do also think Room and SQLite supports binary data as blobs, so you could just declare a column as ByteArray and it should just work.
What I've been doing in my projects is to write them into the internal or external storage of my app and then store the reference Uri as a String to later be able to retrieve the image from disk.
Something that I discourage even more is to stuff more than one value per row; having a list of stuff inside a coulmn in SQL is definetely not a pattern you should follow. Creating a "join" table should be simple enough or simply an extra column you could use to group them by should be easy enough, right?

Save and load object data with NSKeyedArchiver - data loss after object variables changed

I am using NSKeyedArchiver() for saving and loading objects in Swift.
The problem is when the object variables change, like adding new variable into object, the NSKeyedArchiver() cannot decode the last saved objects.
func tripsDataFilePath() -> URL {
return getDocumentsDirectory().appendingPathComponent("Data.plist")
}
The code I am using for saving:
func saveData() {
NSKeyedArchiver.archiveRootObject(data, toFile: dataFilePath().path)
}
and the loading code:
func loadData() {
if let data = NSKeyedUnarchiver.unarchiveObject(withFile: dataFilePath().path) {
project = data as! [Project]
}
}
Is there any way to prevent data loss (can't load last objects) for when changing the object structure?
It is not important when developing first version of iOS app. But imagine that users downloaded the application and in new versions I want to add new features that requires changes to current data objects! then users will loss all datas.
i have find out that the problem is with file format.
i have used this name for saving NSKeyedArchiver Object : "Data.plist"
and when i have removed ".plist" format, the problem has been solved.
Try to check how you decode your data. I was decoding all the data with decodeObject. However, from swift 3.0 you need to be specific how to decode your data. For example, if your data is an Integer, instead of using
let number = aDecoder.decodeObject(forKey: CodingKeys.number) as? Int
you need to use
let number = aDecoder.decodeInteger(forKey: CodingKeys.number)

Firebase push very slow when inserting key that contains large arrays

Heyhou
I'm trying to add some 3D models (buildings) in JSON format to a Firebase from a Node.js server reading the data from a JSON file. I'm using the push() to add a key under buildings.
var buildingsRef = new Firebase('https://test.firebaseIO-demo.com/buildings');
buildingsRef.push({ ... });
Unfortunately, the push is very slow and it takes approx. 30 seconds to insert the JSON into Firebase. The JSON object looks like the following:
{
geometries: {
vertices: [...],
faces: [...],
normals: [....]
},
materials: {
},
object: {
},
metadata: {
}
}
The geometries object contains Arrays for vertices, faces, normals, uv's, etc.. These arrays may have up to 10'000 entries or more, depending on the complexity of the 3D-Model.
I'm not sure if the large JSON itself (file on disk is about 2Mb) or the Array representation is the cause for the slow insertion into Firebase. I suspect that it has something to do with how Firebase internally represents Arrays.
Is there any way to optimize this? I like to store the whole building under one key, so I can check in the frond end of my application if the building has been replaced (usually by the server-side). I don't need to modify the individual arrays, I just like to be able to swap out a building with a different one and let my front-end update accordingly.
Thanks for your help!

Is it possible to store an Array into an EncryptedLocalStore item? AIR

I want to save my Array's strucure and load it the next time I open my AIR application. Is there a way to store it to an EncryptedLocalStore item then get it later when I re-open the app?
EncryptedLocalStore.setItem() method takes a byte array when storing contents. To store an array, just use ByteArray.writeObject() method (as described in http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/ByteArray.html#writeObject()) to convert your Array to a ByteArray - and then persist the same to the ELS.
var array:Array = getArray();
var byteArray:ByteArray = new ByteArray();
byteArray.writeObject(array);
EncryptedLocalStore.setItem('somekey', byteArray);
Hope this helps.
Update: Added code to retrieve the array back.
var byteArray:ByteArray = EncryptedLocalStore.getItem('somekey');
var array:Array = byteArray.readObject() as Array;
Update: For custom classes.
In case you want to serialize your own custom classes to the ByteArray, you may have to call registerClassAlias() before writing the object to the ByteArray. For eg.
registerClassAlias("com.example.eg", ExampleClass);
I have found that it is easiest to to serialize the Array to a string and then store that string in the ELS. Then when you pull it out deserialize it back into an Array.

Resources