美文网首页
SwiftUI入门 - 13. TodoLists数据的本地持久

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

作者: 思跃喵 | 来源:发表于2022-11-30 17:12 被阅读0次
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 的基本使用

相关文章

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

    置顶 菜鸟入门,各位大佬轻喷,如有谬误之处欢迎讨论建议,也欢迎各位道友与我同行 “不积跬步,无以至千里;不积小流,...

  • iOS本地数据持久化

    iOS本地数据持久化 iOS本地数据持久化

  • iOS本地数据持久化

    转载自:CocoaChina - iOS本地数据持久化 本文内容:iOS本地数据持久化的几种类型iOS本地数据持久...

  • iOS本地数据持久化

    本文内容: iOS本地数据持久化的几种类型 iOS本地数据持久化几种类型的应用场景及使用 一.iOS本地数据持久化...

  • iOS本地数据持久化

    本文内容:iOS本地数据持久化的几种类型iOS本地数据持久化几种类型的应用场景及使用 一,iOS本地数据持久化的类...

  • ios数据持久化

    本文内容:iOS本地数据持久化的几种类型iOS本地数据持久化几种类型的应用场景及使用 一.iOS本地数据持久化的类...

  • iOS 数据持久化

    iOS本地数据持久化 本文内容:iOS本地数据持久化的几种类型iOS本地数据持久化几种类型的应用场景及使用 一.i...

  • 简单数据储存 geekband

    数据持久化分为本地和云端.--本地----文件----数据库--云端----ICloud------文件-----...

  • GeekBand iOS开发高级进阶学习笔记(第四周)

    简易数据存储 数据持久化分为本体持久化和云端持久化本体持久化可以存在本地文件或数据库。云端可以存在iCloud,存...

  • Android数据持久化之数据库的使用

    android开发中数据持久化 轻量级数据持久化一般使用sp,大量数据的持久化就必须考虑使用本地数据库了 Andr...

网友评论

      本文标题:SwiftUI入门 - 13. TodoLists数据的本地持久

      本文链接:https://www.haomeiwen.com/subject/vuidfdtx.html