SwiftUI入门 - 13. TodoLists数据的本地持久化

1130-1.png

置顶

菜鸟入门,各位大佬轻喷,如有谬误之处欢迎讨论建议,也欢迎各位道友与我同行

“不积跬步,无以至千里;不积小流,无以成江海”

继续

上文中我们解实现 todo 的详情表单,即在 List 中点击每一项弹出一个 todo 的表单,里面可以修改 todo 的名称等。

但是发现有一个问题,即每次退出程序时,我们已有的 todo 项全部被重置,再次打开又成了默认的那一条。

所以,本文我们将来实现普通数据的本地持久化保存,包括存储与取消存储。效果如下:

在这里插入图片描述

思考

之前我们用过 @AppStorage 来进行持久化存储,但是 @AppStorage 只支持几个基础的数据类型,我们构造的 TodoLists 显然不是个基础类型。

@AppStorage 确实挺好用的,我们拿它来进行初始化或者保存数据是可以的,既然它只支持基础类型,那么我们将 TodoListsJSONEncoder 转化成 String,进行存取应是可行的。

那么基本的实现方案即修改 TodoModel 添加一个 saveData 方法和 clearData 方法,并修改 TodoModelinit 方法 ,优先使用 @AppStorage 中的数据

实现

修改 TodoModel.swfit

import SwiftUI;
// 一定要添加 Encodable 和 Decodable
struct TodoItem:Identifiable,Equatable,Encodable,Decodable{
    // 把 let 改成var,现在需要id可以被修改了
    var id = UUID();
    var name:String ;
    var isFinished:Bool = false;
    var createTime:Int = 0;
    var finishTime:Int = 0;
    var createdAt:String {
        // ...
    }
}

class TodoLists : ObservableObject {    
    // 添加 @AppStorage 参数
    @AppStorage("todoLists") public var store:String = "";
    @Published private(set) var todoList:[TodoItem];
    init(todoList: [TodoItem]) {
        self.todoList = todoList
        // 如果是个空数组,那么先放一个进去
        if(todoList.count == 0 ){
            // 使用 @AppStorage 里面的数据来进行初始化
            if(store == ""){
                // 本身就没有值的时候新增一条默认的
                add(name: "请添加TODO")
            }else{
                // 这里在将 @AppStorage 中的值进行解码,然后放到 todoList 变量上去
                guard (try? self.todoList = JSONDecoder().decode([TodoItem].self, from: store.data(using: .utf8)!)) != nil else{
                    return;
                }
            }
        }
    }
    // 添加一条 todo项,只要名称即可
    func add(name:String){
        // ...
        saveData()
    }
    // 切换todo项的是否完成状态,如果完成状态为true那更新finishTime
    func toggle(item:TodoItem){
        // ...
        saveData()
    }
    // 删除todo
    func delete(offsets: IndexSet,isFinished:Bool = false){
        // ...
        saveData()
    }
    // 更新数据
    func update(item:TodoItem){
        // ...
        saveData()
    }
    // 将数据保存到 @AppStorage
    func saveData(){
        // 将 todoList JSON化后存入 store变量中,利用 @AppStorage 进行存储
        guard let data = try? JSONEncoder().encode(self.todoList) else{
            return;
        }
        store = String(data:data, encoding: .utf8)!;
    }
    // 清除所有的todo项
    func clear(){
        todoList = [];
        saveData();
    }
}

既然已经有了清除所有数据的方法,那么我们就可以在 Setting 页面中添加一个按钮,用于清除所有的 todo 项,同时显示已有多少条

先将 IndexView 中的 EnvironmentObject 挂到最外层,保证 Setting 能够取得到这个全局实例。

//  IndexView.swift
import SwiftUI
struct IndexView: View{
    // ...
    let todos = TodoLists(todoList: [])
    
    var body: some View{
        // ...
        VStack{
            // 一个简单的tabview,底部导航栏
            TabView {
                TodoView()
                    .tabItem {
                        Image(systemName: "list.dash")
                        Text("TODO")
                    }.tag(0)
                SettingView()
                    .tabItem {
                        Image(systemName: "gear.circle")
                        Text("设置")
                    }.tag(1)
            }
            .font(.headline)
        }.environmentObject(todos)
    }
}

修改 Setting.swift 如下:

//  SettingView.swift
import SwiftUI
struct SettingView: View {
    @AppStorage("isLogin") private var isLogin:Bool = false;
    @AppStorage("userName") private var userName:String = "";
    // 从 EnvironmentObject 中 拿出 todos
    @EnvironmentObject var todos:TodoLists;
    var body:some View{
        NavigationView{
            if(isLogin){
                List{
                    // ...
                    Section{
                        HStack{
                            Spacer()
                            Text("清除所有的Todo,共\(todos.todoList.count)条").foregroundColor(.red)
                            Spacer()
                        }.onTapGesture {
                            todos.clear()
                        }
                    }
                    Section{
                        HStack{
                            Spacer()
                            Text("退出登陆").foregroundColor(.red)
                            Spacer()
                        }.onTapGesture {
                            isLogin = false;
                            // 退出登陆时也应当清楚 todos
                            todos.clear()
                        }
                    }
                }.navigationTitle("设置")
            }else{
                Text("请登录").foregroundColor(.red)
            }
        }
    }
}

总结

  1. @AppStorage 的存储时机不确定,可能存在丢失的可能,以后再研究,目前仅实现功能。
  2. @AppStorage 是保存和读取 UserDefaults 变量的一种快捷方式,即本身就是 UserDefaults。但只支持几种基础数据类型,其它的需要自己扩展
  3. SwiftUI 里的 try 有三种方式: try? 会在出错时,返回 nil,不会导致程序崩溃,如果没有错误,会返回一个可选值,try! 会在出错时崩溃,打破错误传播链,当然还有 do...catch
  4. JSONDecoderJSONEncoder 的基本使用
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容