最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ios - How to correctly decode Saved Quests in SwiftUI using UserDefaults? - Stack Overflow

programmeradmin3浏览0评论

Intro to problem:
I’m building a task manager app where users create custom quests, earn points, and redeem rewards. I’m using UserDefaults to persist data. While rewards and points are saving and loading correctly, quests disappear when I leave the view. Debugging indicates encoding is successful, but decoding fails.

What I’ve tried:
• Ensured quest-saving logic is similar to working reward-saving logic.
• Used print statements to confirm encoding success and check saved data.
• Verified UserDefaults contains saved quest data.
• Tried decoding but consistently receive an error.

My code:

import SwiftUI

class PointsManager: ObservableObject {
    @Published var quests: [Quest] = []
    
    init() {
        loadQuests()
    }

    func addQuest(title: String, totalPoints: Int) {
        let newQuest = Quest(title: title, totalPoints: totalPoints)
        quests.append(newQuest)
        saveQuests()
    }

    func saveQuests() {
        do {
            let encoded = try JSONEncoder().encode(quests)
            UserDefaults.standard.set(encoded, forKey: "quests")
            print("Encoding successful")
        } catch {
            print("Encoding failed: \(error.localizedDescription)")
        }
    }

    func loadQuests() {
        guard let savedData = UserDefaults.standard.data(forKey: "quests") else {
            print("No data found")
            return
        }
        do {
            quests = try JSONDecoder().decode([Quest].self, from: savedData)
            print("Decoding successful")
        } catch {
            print("Decoding failed: \(error.localizedDescription)")
        }
    }
}

struct Quest: Identifiable, Codable {
    var id = UUID()
    var title: String
    var totalPoints: Int
}

Question:
Why is decoding failing while encoding succeeds? How can I correctly load and persist quests using UserDefaults in SwiftUI?

Intro to problem:
I’m building a task manager app where users create custom quests, earn points, and redeem rewards. I’m using UserDefaults to persist data. While rewards and points are saving and loading correctly, quests disappear when I leave the view. Debugging indicates encoding is successful, but decoding fails.

What I’ve tried:
• Ensured quest-saving logic is similar to working reward-saving logic.
• Used print statements to confirm encoding success and check saved data.
• Verified UserDefaults contains saved quest data.
• Tried decoding but consistently receive an error.

My code:

import SwiftUI

class PointsManager: ObservableObject {
    @Published var quests: [Quest] = []
    
    init() {
        loadQuests()
    }

    func addQuest(title: String, totalPoints: Int) {
        let newQuest = Quest(title: title, totalPoints: totalPoints)
        quests.append(newQuest)
        saveQuests()
    }

    func saveQuests() {
        do {
            let encoded = try JSONEncoder().encode(quests)
            UserDefaults.standard.set(encoded, forKey: "quests")
            print("Encoding successful")
        } catch {
            print("Encoding failed: \(error.localizedDescription)")
        }
    }

    func loadQuests() {
        guard let savedData = UserDefaults.standard.data(forKey: "quests") else {
            print("No data found")
            return
        }
        do {
            quests = try JSONDecoder().decode([Quest].self, from: savedData)
            print("Decoding successful")
        } catch {
            print("Decoding failed: \(error.localizedDescription)")
        }
    }
}

struct Quest: Identifiable, Codable {
    var id = UUID()
    var title: String
    var totalPoints: Int
}

Question:
Why is decoding failing while encoding succeeds? How can I correctly load and persist quests using UserDefaults in SwiftUI?

Share Improve this question asked Mar 31 at 23:34 RaccoonSkyRaccoonSky 113 bronze badges New contributor RaccoonSky is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 2
  • Hum..., I approved your Question, going first through the 'Staging Ground', but about "Tried decoding but consistently receive an error.", I think I probably fot to ask you to include that Error Msg, => if you can still add that to your Question...? (By editing it into the Question itself (in a Code or Quote Block), not as a Comment...) – chivracq Commented Mar 31 at 23:55
  • 1 stackoverflow/questions/69148980/… – lorem ipsum Commented Apr 1 at 0:59
Add a comment  | 

1 Answer 1

Reset to default -1

Nothing wrong with the code you show. Here is my test code that shows storing and retrieving the data works.

Note, UserDefaults is meant to be for small amount of data, not really suited for an array of structs. Let me know if this example code does not work for you and show the full error you get.


class PointsManager: ObservableObject {
    @Published var quests: [Quest] = []
    
    init() {
        loadQuests()
    }

    func addQuest(title: String, totalPoints: Int) {
        let newQuest = Quest(title: title, totalPoints: totalPoints)
        quests.append(newQuest)
        saveQuests()
    }

    func saveQuests() {
        do {
            let encoded = try JSONEncoder().encode(quests)
            UserDefaults.standard.set(encoded, forKey: "quests")
            print("Encoding successful")
        } catch {
            print("Encoding failed: \(error.localizedDescription)")
        }
    }

    func loadQuests() {
        guard let savedData = UserDefaults.standard.data(forKey: "quests") else {
            print("No data found")
            return
        }
        do {
            quests = try JSONDecoder().decode([Quest].self, from: savedData)
            print("Decoding successful quests: \(quests)") // <--- here
        } catch {
            print("Decoding failed: \(error)")
        }
    }
}

struct Quest: Identifiable, Codable {
    var id = UUID()
    var title: String
    var totalPoints: Int
}


struct ContentView: View {
    @StateObject private var manager = PointsManager()
    
    var body: some View {
        VStack (spacing: 55) {
            List(manager.quests) { quest in
                HStack {
                    Text(quest.title)
                    Spacer()
                    Text("\(quest.totalPoints)")
                }
            }
            Button("add Quest") {
                let title = String(UUID().uuidString.prefix(10))
                manager.addQuest(title: title, totalPoints: Int.random(in: 0...123))
            }
            Button("check Quest") {
                print("---> quests: \(manager.quests)")
            }
        }
    }
}

发布评论

评论列表(0)

  1. 暂无评论