SwiftUI之CoreData详解(一)

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) inif let error = error as NSError? {fatalError("Unresolved error \(error), \(error.userInfo)")}
})

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

import CoreDatastruct PersistenceController {static let shared = PersistenceController()let container: NSPersistentContainerinit(inMemory: Bool = false) {container = NSPersistentContainer(name: "CoredataTest")if inMemory {container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")}container.loadPersistentStores(completionHandler: { (storeDescription, error) inif 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 = namestudent.age = agestudent.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 NSErrorfatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}

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

//
//  Persistence.swift
//  CoredataTest
//import CoreDatafunc addStudent(viewContext: NSManagedObjectContext, name: String, age: Int16, sex:Int16 = 1){let student = Student(context: viewContext)student.id = UUID()student.name = namestudent.age = agestudent.sex = age
}struct PersistenceController {static let shared = PersistenceController()static var preview: PersistenceController = {let result = PersistenceController(inMemory: true)let viewContext = result.container.viewContextaddStudent(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 NSErrorfatalError("Unresolved error \(nsError), \(nsError.userInfo)")}return result}()let container: NSPersistentContainerinit(inMemory: Bool = false) {container = NSPersistentContainer(name: "CoredataTest")if inMemory {container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")}container.loadPersistentStores(completionHandler: { (storeDescription, error) inif 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.viewContextaddStudent(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 NSErrorfatalError("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.sharedvar 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 CoreDatastruct 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 inNavigationLink {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 NSErrorfatalError("Unresolved error \(nsError), \(nsError.userInfo)")}}}
}#Preview {ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
}

在这里插入图片描述

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux-信号3_sigaction、volatile与SIGCHLD

文章目录 前言一、sigaction__sighandler_t sa_handler;__sigset_t sa_mask; 二、volatile关键字三、SIGCHLD方法一方法二 前言 本章内容主要对之前的内容做一些补充。 一、sigaction #include <signal.h> int sigaction(int signum, const struct sigaction *act,struc…

微服务:Feign篇

1.什么是Feign Feign是一种声明式、模板化的HTTP客户端&#xff0c;可用于调用HTTP API实现微服务之间的远程服务调用。它的特点是使用少量的配置定义服务客户端接口&#xff0c;可以实现简单和可重用的RPC调用。 先来看我们以前利用RestTemplate发起远程调用的代码&#xff…

【leetcode热题】分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 每个孩子至少分配到 1 个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果&#xff0c;计算并返回需要准备的…

使用Visual Studio 2022 创建lib和dll并使用

概述&#xff1a;对于一个经常写javaWeb的人来说,使用Visual Studio似乎没什么必要&#xff0c;但是对于使用ffi的人来说&#xff0c;使用c或c编译器&#xff0c;似乎是必不可少的&#xff0c;下面我将讲述如何用Visual Studio 2022 来创建lib和dll&#xff0c;并使用。 静态库…

前端面试 跨域理解

2 实现 2-1 JSONP 实现 2-2 nginx 配置 2-2 vue 开发中 webpack自带跨域 2 -3 下载CORS 插件 或 chrome浏览器配置跨域 2-4 通过iframe 如&#xff1a;aaa.com 中读取bbb.com的localStorage 1)在aaa.com的页面中&#xff0c;在页面中嵌入一个src为bbb.com的iframe&#x…

详细分析Linux内存知识并释放内存

目录 前言1. 基本知识1.1 free1.2 cat /proc/meminfo1.3 slabtop 2. 清空内存 前言 本篇文章主要分析内存 如果是磁盘空间&#xff0c;推荐阅读&#xff1a;服务器出现根目录磁盘满了解决方法 1. 基本知识 在Linux系统中&#xff0c;查看内存的基本知识包括以下几个方面&…

Rabbitmq消息丢失-消费者消息丢失(二)

说明&#xff1a;消费端在处理消息的过程中出现异常&#xff0c;例如&#xff1a;业务逻辑异常&#xff0c;或者消费者被停机&#xff0c;或者网络断开连接等&#xff0c;以上等情况使消息没有得到正确恰当的处理&#xff0c;也会使消息丢失。 分析&#xff1a;分析就是说明中…

第 387 场 LeetCode 周赛题解

A 3069. 将元素分配到两个数组中 I 模拟 class Solution { public:vector<int> resultArray(vector<int> &nums) {vector<int> r1{nums[0]}, r2{nums[1]};for (int i 2; i < nums.size(); i) {if (r1.back() > r2.back())r1.push_back(nums[i]);e…

WPF中如何设置自定义控件(二)

前一篇文章中简要讲解了圆角按钮、圆形按钮的使用,以及在windows.resource和app.resource中设置圆角或圆形按钮的样式。 这篇主要讲解Polygon(多边形)、Ellipse(椭圆)、Path(路径)这三个内容。 Polygon 我们先看一下的源码: namespace System.Windows.Shapes { pu…

Vue3学习记录(三)--- 组合式API之生命周期和模板引用

一、生命周期 1、简介 ​ 生命周期&#xff0c;指的是一个 Vue 实例从创建到销毁的完整阶段&#xff0c;强调的是一个时间段。 ​ 生命周期钩子函数&#xff0c;指的是 Vue 实例提供的内置函数&#xff0c;函数的参数为一个回调函数。这些钩子函数会在实例生命周期的某些固定…

Spring(22) Spring中的9种设计模式

目录 一、简单工厂模式&#xff08;Simple Factory&#xff09;二、工厂方法模式&#xff08;Factory Method&#xff09;三、单例模式&#xff08;Singleton&#xff09;四、适配器模式&#xff08;Adapter&#xff09;五、代理模式&#xff08;Proxy&#xff09;七、观察者模…

【ARM Trace32(劳特巴赫) 高级篇 21 -- SystemTrace ITM 使用介绍】

文章目录 SystemTrace ITMSystemTrace ITM 常用命令Trace Data AnalysisSystemTrace ITM CoreSight ITM (Instrumentation Trace Macrocell) provides the following information: Address, data value and instruction address for selected data cyclesInterrupt event info…