SwiftUI之CoreData详解(一)

news2024/11/15 3:34:04

coreData 是一种数据持久化的方案,是对SQLite的一种封装。一说到这种桌面化的数据库,我就无比的怀念Foxbase|Foxpro, 多好的数据库产品,被微软扼杀了,相当年教大学生妹子们国家二级数据库时都是手把手教的,呃~~~,不好意思,说的有点跑题了。要想使用coreData, 首先我们要先创建一个模型文件,它是一个.xcdatamodeld结尾的文件,这个模型文件说白了就是一个库,是的,因为coreData的本质就是一个关系数据库模型,只不过在这里把各种名称改了个名字而已(题外话,苹果公司以为这样做更显得更高大上,依我看其实真没这个必要,只会增加开发者的学习负担)。数据表也不叫数据表,叫实体(entity), 字段也不叫字段,叫属性(atributte),你看,苹果就是这么的特立独行,没办法,谁让它的系统设计的那么的招人爱呢。其实这些叫法都没问题,因为在关系数据库中这个名词都是合法的,只是大部分开发者习惯了一种规则。
首先,我们创建项目coreDataTest
在这里插入图片描述

Storage中创建 coreData, 关于swiftData,我会在后面的文章个讲解,因为这个需要最新的系统才能使用。
在这里插入图片描述

如果没有选也没关系,也可以手动创建,只不过会麻烦一些,选择后系统会给我们自动创建一些代码。
在这里插入图片描述

下一步在选择保存目录时如果勾选 Create Git repository on my Mac, 则创建项目的同时会创建 git 仓库。 关于 git 仓库的使用请查看我往期的文章。

这样我们的项目就创建好了,在项目中会有一个 coreDataTest.xcdatamodeld的文件,这个就是coredata的模型文件,也就是本地数据库文件。
在这里插入图片描述

如果是手动创建,请看下面的步骤:

创建模型

一般在创建项目的开始,如果你勾选了 coreData 这个选项,那么你的项目中就有一个和项目同名的 .xcdatamodeld ,如何你没有选择,那么我们就要创建它。创建它很简单,通过 文件 -> 新建 -> 文件 -> Data Model创建一个模型, 如下所示:
在这里插入图片描述

在底部有两个按钮,一个在左侧是 addEntity 用于增加新的实体(数据表格), 一个在右侧是 add Attribute 用于添加一个属性(字段)。我们点击 addEntity 增加一个数据表, 然后双击表名修改名称,名称的第一个字符一定要大写。 要想删除一个数据表,直接按键盘上的 delete

添加实体与属性

默认的有一个Item实体了,做为测试,我们再创建一个 Student 的实体,并添加以下属性:

  • id: UUID
  • name: String
  • age: Int16
  • sex: Int16

如下所示:
在这里插入图片描述

目前关于其它功能暂时不用管它。现在一个基本的数据库已经设计好了,下面我们看看如何使用它。

访问控制器

coreData 要用一个控制器才能访问它,也叫上下文容器, 这个容器类型为 NSPersistentContainer

如下所示:

let container: NSPersistentContainer
container = NSPersistentContainer(name: "CoredataTest")

NSPersistentContainer(name: "CoredataTest")里面传的就是我们数据模型文件的名称,可以有多个数据模型,只要把相应的名称传给它就行了,这样我们就有了引用这个数据模型的上下文,也就是数据环境。如果我们想让这个数据模型存在内存里而不是文件中,那么就要这么设置:

container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")

然后就是加载数据了:

container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    if let error = error as NSError? {
        fatalError("Unresolved error \(error), \(error.userInfo)")
    }
})

闭包中的 storeDescription 是路径信息, error 是异常详情。根据以上内容,我们封装一下:

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()
    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "CoredataTest")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

这样一个数据的控制器就创建好了,我们现在来创建一些数据存储一下, 由于属性是相同的,我们这里创建一个函数:

func addStudent(viewContext: NSManagedObjectContext, name: String, age: Int16, sex:Int16 = 1){
    let student = Student(context: viewContext)
    student.id = UUID()
    student.name = name
    student.age = age
    student.sex = age
}

然后像这样调用:

let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
addStudent(viewContext: viewContext, name: "张三", age: 23, sex: 1)
addStudent(viewContext: viewContext, name: "李四", age: 26, sex: 1)
addStudent(viewContext: viewContext, name: "王二", age: 19, sex: 2)
addStudent(viewContext: viewContext, name: "麻子", age: 20, sex: 2)
addStudent(viewContext: viewContext, name: "刘七", age: 29, sex: 1)
do {
    try viewContext.save()
} catch {
    let nsError = error as NSError
    fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}

大至的过程我们已经清楚了,现在我们来整合一下,创建一个swift文件,这个文件名没有特别要求,建议以大写开头的文件名,这里用系统给定的名称:Persistence.swft

//
//  Persistence.swift
//  CoredataTest
//

import CoreData

func addStudent(viewContext: NSManagedObjectContext, name: String, age: Int16, sex:Int16 = 1){
    let student = Student(context: viewContext)
    student.id = UUID()
    student.name = name
    student.age = age
    student.sex = age
}

struct PersistenceController {
    static let shared = PersistenceController()

    static var preview: PersistenceController = {
        let result = PersistenceController(inMemory: true)
        let viewContext = result.container.viewContext
        addStudent(viewContext: viewContext, name: "张三", age: 23, sex: 1)
        addStudent(viewContext: viewContext, name: "李四", age: 26, sex: 1)
        addStudent(viewContext: viewContext, name: "王二", age: 19, sex: 2)
        addStudent(viewContext: viewContext, name: "麻子", age: 20, sex: 2)
        addStudent(viewContext: viewContext, name: "刘七", age: 29, sex: 1)
        
        do {
            try viewContext.save()
        } catch {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "CoredataTest")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

如果你在创建项目的开始选择了coreData,其实这个文件会自动创建。只是为了后面的实例我做了少许的改动。上面的结构代码中的如下代码

static var preview: PersistenceController = {
    let result = PersistenceController(inMemory: true)
    let viewContext = result.container.viewContext
    addStudent(viewContext: viewContext, name: "张三", age: 23, sex: 1)
    addStudent(viewContext: viewContext, name: "李四", age: 26, sex: 1)
    addStudent(viewContext: viewContext, name: "王二", age: 19, sex: 2)
    addStudent(viewContext: viewContext, name: "麻子", age: 20, sex: 2)
    addStudent(viewContext: viewContext, name: "刘七", age: 29, sex: 1)

    do {
        try viewContext.save()
    } catch {
        let nsError = error as NSError
        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
    }
    return result
}()

是为预览做的准备数据。如果你不想预览,只想实时真机调试的话这个可以省略。

container.viewContext.automaticallyMergesChangesFromParent = true

这句的作用是实时的将数据的变动反映到Context中。也就是实时刷新的意思。

从上面的可以看出,要想操作coreData, 就必须先获取这个控制器的context, 即上下文。做为程序的单列执行环境,我们可以把它存到环境变量中,以方便不同的程序获取。所以,我们在项目的App中(即入口程序)把这个句柄注册到环境变量中:

CoredataTestApp.swift

//
//  CoredataTestApp.swift
//  CoredataTest
//

import SwiftUI

@main
struct CoredataTestApp: App {
    let persistenceController = PersistenceController.shared

    var body: some Scene {
        WindowGroup {
            ContentView()
            .environment(\.managedObjectContext,persistenceController.container.viewContext)
        }
    }
}

这样,我们在 ContentView视图中就可以直接这样获取到这个context:

@Environment(\.managedObjectContext) private var viewContext

为了简化流程,现在的问题就差一个,就是如何把数据反映到我们的UI中来,这就要用到查询语句了,当然这是swiftUI中封装好的。

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Student.id, ascending: true)],
    animation: .default)
private var items: FetchedResults<Student>

我们把@FetchRequest当作是变量的申明注解就行了。keyPath是用于唯一标识的id号,只要是能唯一标识数据记录就行, 这样items就是所有数据的集合了。

ContentView.swift

//
//  ContentView.swift
//  CoredataTest
//

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext

    @FetchRequest(
        sortDescriptors: [NSSortDescriptor(keyPath: \Student.id, ascending: true)],
        animation: .default)
    private var items: FetchedResults<Student>

    var body: some View {
        NavigationView {
            List {
                ForEach(items) { item in
                    NavigationLink {
                        Text("学生 在 \(item.id!)")
                    } label: {
                        Text(item.name!)
                    }
                }
                .onDelete(perform: deleteItems)
            }
        }
    }

    private func deleteItems(offsets: IndexSet) {
        withAnimation {
            offsets.map { items[$0] }.forEach(viewContext.delete)

            do {
                try viewContext.save()
            } catch {
                let nsError = error as NSError
                fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
            }
        }
    }
}

#Preview {
    ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}

在这里插入图片描述

当然,这只是开胃小菜,好戏还没上场。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1503111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决火狐浏览器访问地址受限制问题(This address is restricted)

问题如下图&#xff1a; This address is restrictedThis address uses a network port which is normally used for purposes other than Web browsing. Firefox has canceled the request for your protection. 此地址受到限制 此地址使用通常用于 Web 浏览以外的目的的网…

21.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-配置数据保存到文件

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a; 易道云信息技术研究院VIP课 上一个内容&#xff1a;20.数据分析工具…

人才推荐 | 毕业于美国凯斯西储大学的博士,专业知识广泛

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

前端使用Ant Design中的Modal框实现长按顶部拖动弹框需求

需求&#xff1a;需要Ant Design中的一个Modal弹框&#xff0c;并且可以让用户按住顶部实现拖动操作 实现&#xff1a;在Ant Design的Modal框的基础上&#xff0c;在title中添加一个onMouseDown去记录拖拽的坐标&#xff0c;然后将其赋值给Modal的style属性 代码部分&#xff…

没有面试邀约还在怪学历?帮助1万人通过面试的简历实战经验

很多人说自己明明投了很多公司的简历&#xff0c;但是都没有得到面试邀请的机会。自己工作履历挺好的&#xff0c;但是为什么投自己感兴趣公司的简历&#xff0c;都没有面试邀请的机会。反而是那些自己没有投递的公司&#xff0c;经常给自己打电话。 造成这些现象的主要原因&am…

解决白屏问题:让你的网站重焕生机

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Ubuntu上安装任意版本nodejs方法

在Ubuntu中安装指定版本的Node.js&#xff0c;可以使用Node Version Manager (NVM)。以下是安装步骤&#xff1a; 首先&#xff0c;安装NVM。在命令行中输入&#xff1a; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash 这个命令会下载并…

Maya人物建模

【MAYA人物建模】超详细讲解人物嘴巴、鼻子、眼睛、耳朵、头发、帽子等布线细节&#xff0c;零基础人物头部布线教程_哔哩哔哩_bilibili 原始图像凑合用&#xff0c;视屏中截图 图像导入过程技巧总结 前视图/右视图模式下导入图形 创建图层 锁定后可以避免图片位置的移动 前视…

【Pytorch、torchvision、CUDA 各个版本对应关系以及安装指令】

Pytorch、torchvision、CUDA 各个版本对应关系以及安装指令 1、名词解释 1.1 CUDA CUDA&#xff08;Compute Unified Device Architecture&#xff09;是由NVIDIA开发的用于并行计算的平台和编程模型。CUDA旨在利用NVIDIA GPU&#xff08;图形处理单元&#xff09;的强大计算…

外包干了10天,技术退步明显···

先说一下自己的情况&#xff0c;本科生&#xff0c;通过校招进入杭州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试&…

⭐每天一道leetcode:69.x的平方根(简单;不使用sqrt函数)

⭐今日份题目 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 注意&#xff1a;不允许使用任何内置指数函数和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0.5 。 示…

Return On Investment (ROI)

Return On Investment &#xff08;ROI&#xff09;投资回报率 ROI (A - B) / A

图片去水印软件 Inpaint使用教程

Inpaint中文版是一款功能强大&#xff0c;实用方便的图片快速去水印软件&#xff0c;Inpaint中文版操作简单易上手&#xff0c;可以帮助你达到一键美化图片的效果。图片水印现在已经非常流行&#xff0c;能够借助一款不错的图片水印去除工具可以让图片处理工作简单便捷。本文讲…

AI大模型解锁写作技能,成为网红不是梦

AI大模型的出现改变了人们的生活方式&#xff0c;包括写作技能的提升。如今&#xff0c;想要成为网红并不是遥不可及的梦想。借助AI大模型&#xff0c;可以轻松地解锁写作技能&#xff0c;迅速积累粉丝&#xff0c;开启网红之路。 AI大模型拥有强大的计算和学习能力&#xff0…

看了很多文章,就这篇说明白了什么是接口测试!

接口&#xff08;API&#xff09;是一个简称&#xff0c;全名叫应用程序编程接口(Application Programming Interface)&#xff0c;是一些预先定义的函数。目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力&#xff0c;而又无需访问源码&#xff0c;或理解…

基于Springboot的志愿服务管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的志愿服务管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

【Redis知识点总结】(三)——Redis持久化机制、内存淘汰策略、惰性删除机制

Redis知识点总结&#xff08;三&#xff09;——Redis持久化机制、内存淘汰策略、惰性删除机制 Redis持久化RDBAOFAOF与RDB的对比混合持久化 内存淘汰策略惰性删除机制 Redis持久化 Redis有两种数据持久化的方式&#xff0c;一种是RDB、一种是AOF。 RDB RDB是内存快照&#…

数据结构 - 堆(优先队列)+ 堆的应用 + 堆练习

文章目录 前言堆一、什么是堆二、堆又分为大根堆和小根堆三、由于堆的逻辑结构被看成完全二叉树&#xff0c;那么我们先来了解一下完全二叉树。四、堆使用数组还是链表储存数据呢&#xff1f;五、数组构建二叉树和父子节点之间的定位六、堆进行的操作七、实现小根堆1、堆的初始…

【Spring Boot 源码学习】BootstrapContext的实际使用场景

《Spring Boot 源码学习系列》 BootstrapContext的实际使用场景 一、引言二、往期内容三、主要内容3.1 BootstrapContext3.2 BootstrapRegistry 初始化器实现3.3 BootstrapContext 的实际使用场景3.3.1 早期启动时3.3.2 环境配置准备完成时3.3.3 应用上下文准备完成后关闭 Boot…

如果要填写邀请码,但是不想新建窗体 还要有确定和取消按钮和逻辑判断使用模态对话框即可

如果要填写邀请码&#xff0c;但是不想新建窗体 还要有确定和取消按钮和逻辑判断 如果你不想新建窗体来填写邀请码&#xff0c;但又希望有确定和取消按钮以及逻辑判断&#xff0c; 如果你不想新建窗体&#xff0c;并且希望在需要时显示一个临时的文本框来填写邀请码&#xff…