I have the below code that saves my array to UserDefaults at app startup and loads the data correctly, I use the propertyObserver didSet, hoping that any time a value is reset in value type struct , this will create a change in the @Published array of this type, so far so good, fast forward,
Now I need a way to add favourites , I do so by using a @Binding of a that specific song passed from the album View, I toggle it and it works, but only as long I do not relaunch the app, how can I make this change permanent , like in CoreData, I do not know how I can specifically change UserDefault value, thanks
Where I use UserDefaults to save the array
struct Song: Identifiable, Codable {
var id = UUID()
var name: String
var album: String
var isFavorite: Bool
var genre: String
var artist: String
init(name: String, album: String, isFavorite: Bool, genre: String, artist: String) {
self.name = name
self.album = album
self.artist = artist
self.genre = genre
self.isFavorite = isFavorite
}
static let `default` = Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo")
}
class Songs: ObservableObject {
var songsData: [Song] =
[
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
Song(name: "demo", album: "demo", isFavorite: false, genre: "demo", artist: "demo"),
]
@Published var songs = [Song]() {
didSet {
if let encoded = try? JSONEncoder().encode(songsData) {
UserDefaults.standard.set(encoded, forKey: "demosongs")
}
}
}
init(){
if let savedItems = UserDefaults.standard.data(forKey: "demosongs") {
if let decodedItems = try? JSONDecoder().decode([Song].self, from: savedItems) {
songs = decodedItems
return
}
}
songs = []
}
}
Where I try to set the song as favourite but this change is not permanent , how can I make it permanent by making changes to UserDefaults for the specific record
struct SongView: View {
@Binding var song: Song
var body: some View {
Text("\(song.name)")
Button(song.isFavorite ? "Remove from favorite" : "add to favorites") {
song.isFavorite.toggle()
}
Text("Hello World")
}
}
// Edit for vadian
I have the album view if I use songs instead of songs data , this view is then empty
struct AlbumView: View {
@ObservedObject var songData: Songs
var body: some View {
ZStack {
Image("cover")
.resizable().opacity(0.5)
.zIndex(1)
ScrollView {
VStack {
ForEach($songData.songs) { $song in
NavigationLink {
SongView(song: $song, songs: Songs())
} label: {
customText(image: "joinus", str: song.name)
.frame(maxWidth: .infinity)
}
}
}.padding()
}.zIndex(2)
}
}
func customText(image: String, str: String) -> some View {
Group {
VStack {
RoundedRectangle(cornerRadius: 20)
.fill(.blue)
.padding()
.frame(width: 300, height: 150)
.overlay(Image(image)
.resizable()
.frame(width: 300, height: 150))
Text(str)
.scaledFont(name: "Gothic", size: 20)
.foregroundColor(.white)
.padding()
.background(RoundedRectangle(cornerRadius: 20, style: .continuous).fill(Color.gray))
}
}
}
}
CodePudding user response:
You save always the (unmodified) songsData
array. It must be
if let encoded = try? JSONEncoder().encode(songs) { ...
And you have to change the init
method to assign the default array if nothing is saved yet in UserDefaults
init(){
if let savedItems = UserDefaults.standard.data(forKey: "demosongs"),
let decodedItems = try? JSONDecoder().decode([Song].self, from: savedItems) {
songs = decodedItems
} else {
songs = songsData
}
}