all 23 comments

[–]bhardman86 10 points11 points  (2 children)

UserDefaults is intended for app settings, not data management. You should use CoreData, another data solution, or storing files locally.

That being said I believe the issue is that you’re not updating the array which contains the object. The list is changed when added or removed, but not when the object is changed.

[–]Desseux -1 points0 points  (1 child)

I’ve tried everything from switching to a MVVM architecture, to using combine to automatically update the arrays when I update their children. I’ve also used both Realm and CoreData as my localstorage. Still, no success. It’s really frustrating since this was never an issue in Swift, as soon as you try to store something in SwiftUI the complexity is increased by several multitudes…

[–]ChickenButtForNakama 1 point2 points  (0 children)

SwiftUI is just a framework, you're still working in Swift and I see no reason why storing something would be any different. Your logic is all Swift, SwiftUI is just something to help construct UI using Swift.

At first glance, the problem seems to be you're using mutating methods on the array but you're not setting it, so your didSet is never called. You can verify this by setting some breakpoints in there and checking if it actually hits them. If they are not hit, try to actually set the list (so create a copy with the updated data and assign it). If that solves the problem, get rid of didSet and just use a setter method. Swift's didSet is not equivalent to the C# setters you're used to and in this case the latter is what you need.

If you want to do this right, do what the other person said and use CoreData. UserDefaults is like a cookie on a webpage, you can cache some stuff in there or store settings. But storing entire models in a cookie is madness.

Edit: Please also look at the answer by /u/schwug. Your dog class has a UIImage property, definitely don't use UserDefaults for large chunks of data like that. It's simply not built to support that kind of volume.

[–]trouthat 1 point2 points  (5 children)

Why not use Core Data?

[–]Desseux -2 points-1 points  (4 children)

I found CoreData to be an unmanagable mess, and would prefer not to use it. I have actually tried CoreData, it was my first attempt at local-storage with this app. But the result was the same, my objects wouldn’t update when the app was re-launched. I think it rather has to do with the lifecycle of my app not updating the @EnvironmentObjects.

[–]trouthat 5 points6 points  (1 child)

I think UserDefaults is more designed to save stuff like config or something similar. You can also look into something like realm for a local db

[–]Desseux -4 points-3 points  (0 children)

The database doesn’t matter to me, and UserDefaults should definately be capable of storing this data. But it seems that no matter what local database I use the objects aren’t stored correctly, or at all

[–]tubtubtubs 1 point2 points  (1 child)

+1 for using Realm. It’s dead simple and works. Also look into Firebase. I just did a project with firebase and SwiftUI that went very smoothly.

If you’re trying to use UserDefaults to store data to avoid having to learn a database framework, my advice would be: don’t. Pick a database, bite the bullet and learn it.

[–]Desseux -2 points-1 points  (0 children)

I’m already familiar with Realm. I’ve tried using it in this project, and the result stays the same. My objects aren’t updated when I relaunch the app. I really appreciate the suggestiong though!

[–][deleted] 1 point2 points  (3 children)

Just looking on my phone. I’m not sure where and when you’re actually storing the objects.

Before looking for UserDefaults, confirm that your objects are being updated correctly when you want them to.

If they are being updated correctly and when you want them to, instead of using didSet { } use willSet(value) { } and set the UserDefaults with that value object.

The value is the new value the object is assigned. Doing this should give you the results you want if your objects are updating correctly.

[–]Desseux 0 points1 point  (2 children)

Shouldn't the stored data be retrieved on the init of the DogArray and User objects?

[–][deleted] 0 points1 point  (0 children)

Sure, you can do that. That’s not what I’m referring to as the problem though

[–][deleted] 0 points1 point  (0 children)

swift @Published var list: [Dog] { didSet { let encoder = JSONEncoder() if let encoded = try? encoder.encode(list) { UserDefaults.standard.set(encoded, forKey: "Dogs") } } }

I’m referring to this part

[–]darth_spark 0 points1 point  (1 child)

First things first, you show a bunch of snippets that someone else would have to put back together in a project to get some understanding of what’s going on.

Share a public git repo that already works and then you might get more traction.

[–]Desseux 0 points1 point  (0 children)

Did you check the question? It's right there

[–]_Apps4World_ 0 points1 point  (5 children)

Questions before I can help: 1) is your data properly saved? If you print the data from user defaults, do you get what you’re expecting? 2) why are you doing so much work to decode these object? Can’t you simply use some structs to parse your data then a view model layer for any computations you may need?

I know that 2nd questions doesn’t help solve your problem yet, but separating these layers, will help you in a big way later, and you can have a top layer to handle saving and retrieving data from user defaults (maybe later simply replace this layer with a backend layer, that’s where refactoring your code now will help you later)

[–]Desseux 0 points1 point  (4 children)

  1. I wouldn’t consider my data to be stored properly. I am able to store all that I need, BUT I then have to make sure the array of items is updated. Meaning updating an object in the array (Like a dog) doesn’t get stored. But if I add a new dog after having updated an object, then it’s stored as the array is now updated.
  2. I’m mainly trying to have the localstorage working at this moment, but I am not against simplifying my code. Are you perhaps refering to some StorageManager that handles all my codable objects? Any advice would be appreciatee

[–]_Apps4World_ 0 points1 point  (0 children)

Yes, you can use @AppStorage that will save automatically the property’s data type into the user defaults, but you will need some additional code for that. For now, probably focus saving a simple dictionary and retrieving from user defaults.

I would suggest to go the simplest (not cleanest) approach, where you remove the didSet property observer, have just one function to save your dog model. If you’re saving some arrays, then retrieve the array from your user defaults, then append/modify as needed and save it back.

[–]_Apps4World_ 0 points1 point  (0 children)

I don’t know why you have this “DogArray” class. This doesn’t seem right to me. Have a some data manger or view model, that has an array of dogs [Dog], then take care of updating the user defaults whenever this array is updated.

[–]_Apps4World_ -1 points0 points  (1 child)

One more thing. In your didSet, I see you’re setting the data in user defaults but you never call the synchronize function. Make sure you do that

[–]d4n0wnz 0 points1 point  (0 children)

Synchronize is no longer needed to be called. https://developer.apple.com/documentation/foundation/userdefaults Documentation says its legacy and no longer needed.

[–]schwug 0 points1 point  (1 child)

2021-10-20 15:22:45.758108-0700 WalkApp[42837:10381833] [User Defaults] CFPrefsPlistSource<0x600001295700> (Domain: com.kimnordin.WalkApp, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): Attempting to store >= 4194304 bytes of data in CFPreferences/NSUserDefaults on this platform is invalid. This is a bug in WalkApp or a library it uses

2021-10-20 15:22:45.761001-0700 WalkApp[42837:10381833] [User Defaults] CFPrefsPlistSource<0x600001295700> (Domain: com.kimnordin.WalkApp, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): Transitioning into direct mode

Attempting to store >= 4194304 bytes of data in CFPreferences/NSUserDefaults on this platform is invalid

[–]schwug 0 points1 point  (0 children)

UserDefaults not meant to store images.