You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI如何实现用户自定义数组持久化存储,应用重启后数据仍保留?

收藏列表持久化实现方案

@AppStorage 不支持直接存储自定义类型数组,轻量场景下可以通过「Codable 序列化 + UserDefaults 存储」的方案实现数据持久化,无需引入复杂的数据库方案,实现步骤如下:


步骤1:给自定义类型添加 Codable 协议

首先确保你的 Categories 结构体遵守 Codable 协议,用于数组的序列化和反序列化,你已经用到了 firstIndex、contains 方法说明已经遵守了 Equatable,直接加协议即可:

struct Categories: Identifiable, Equatable, Codable {
    // 原有属性保持不变即可,String、Int等基础类型默认支持Codable
}

步骤2:给 ViewModel 添加持久化逻辑

修改你的 CategoriesModel,新增本地读写逻辑,初始化时自动读取已存储的收藏数据:

class CategoriesModel: ObservableObject {
    // 存储用的固定Key,避免写错
    private let saveKey = "savedRestaurantList"
    
    // 可以通过didSet实现修改自动存,无需手动调用保存方法
    @Published var savedList: [Categories] = [] {
        didSet {
            saveToLocal()
        }
    }
    
    init() {
        // 初始化时从本地读取已保存的收藏列表
        loadFromLocal()
    }
    
    // 从本地读取收藏列表
    private func loadFromLocal() {
        guard let data = UserDefaults.standard.data(forKey: saveKey) else {
            // 本地没有存储过数据,返回空数组
            savedList = []
            return
        }
        do {
            savedList = try JSONDecoder().decode([Categories].self, from: data)
        } catch {
            print("收藏列表读取失败: \(error.localizedDescription)")
            savedList = []
        }
    }
    
    // 把当前收藏列表保存到本地
    private func saveToLocal() {
        do {
            let data = try JSONEncoder().encode(savedList)
            UserDefaults.standard.set(data, forKey: saveKey)
        } catch {
            print("收藏列表保存失败: \(error.localizedDescription)")
        }
    }
}

步骤3:优化原有业务代码(可选,但建议修改)

你原有代码里的强制解包、单独维护的isSaved状态都有潜在风险,可以优化如下:

struct DetailView2: View {
    var restaurantInfo: Categories
    @EnvironmentObject var a: CategoriesModel
    // 不需要单独维护isSaved状态,直接判断数组是否包含当前餐厅即可,避免状态不同步

    var body: some View {
        VStack {
            ZStack(alignment: .bottomLeading) {
                Image(restaurantInfo.pic)
                    .resizable()
                    .scaledToFit()
                Button(action: {
                    if let index = a.savedList.firstIndex(of: restaurantInfo) {
                        // 已收藏,执行取消
                        a.savedList.remove(at: index)
                    } else {
                        // 未收藏,执行添加
                        a.savedList.append(restaurantInfo)
                    }
                    // 因为ViewModel里加了didSet自动保存,这里不需要额外调用保存方法
                }, label: {
                    Image(systemName: a.savedList.contains(restaurantInfo) ? "bookmark.fill" : "bookmark")
                        .padding()
                        .background(Color.blue)
                        .foregroundColor(.white)
                        .cornerRadius(12)
                        .padding()
                })
            }
            // 其余原有代码保持不变
        }
    }
}

补充说明

如果你的收藏数据量超过千条、或者需要做更复杂的查询筛选,可以改用 SwiftData 或 CoreData 实现,对于常规收藏需求,上述方案性能足够、实现成本最低。

内容的提问来源于stack exchange,提问作者sadel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 13:06:07