iOS 18 中全新 SwiftData 重装升级,其中一个功能保证你们“爱不释手”

news2024/11/28 22:48:30

在这里插入图片描述

概览

在最新的 WWDC 2024 中,苹果对多个系统框架都做了重量级的功能升级。这怎么能够少了 SwiftData 这位“后起之秀”呢?

在这里插入图片描述

万象更新的 iOS 18 为 SwiftData 增加了全新的唯一性、自定义数据仓库、富表达式以及字段索引等超赞功能。

在本篇博文中,您将学到如下内容:

  • 概览
  • 1. 什么是 SwiftData?
  • 2. 新的 #Unique 宏
  • 3. 历史数据操作记录
  • 4. 自定义数据仓库(Data Stores)
  • 5. Xcode 预览 Traits
  • 6. 自定义额外数据 Queries
  • 7. #Expression 表达式宏
  • 8. #Index 宏
  • 总结

闲言少叙,让我们马上一起跃入 SwiftData 焕然一新的世界中吧!

Let‘s go!!!😉


1. 什么是 SwiftData?

在去年苹果乘着 iOS 17 的东风祭出了全新纯 Swifty 范儿的数据库 SwiftData。

在这里插入图片描述

利用 SwiftData 我们仅用寥寥几行描述性代码就可以易如反掌的构建出一个“羽翼丰满”的数据库支持应用。

SwiftData 内置了海量的功能特性,我们可以用它们来搭建本地或者 iCloud 支持的复杂数据库 App。
在这里插入图片描述

如下代码所示,我们借助 @Model 宏将平淡无奇的 Trip、BucketListItem 以及 LivingAccommodation 类转换为了持久存储背后“撑腰”的 SwiftData 类型:

// Trip Models decorated with @Model
import Foundation
import SwiftData

@Model
class Trip {
    var name: String
    var destination: String
    var startDate: Date
    var endDate: Date
    
    var bucketList: [BucketListItem] = [BucketListItem]()
    var livingAccommodation: LivingAccommodation?
}

@Model
class BucketListItem {...}

@Model
class LivingAccommodation {...}

有了上面的类型定义,我们就可以自然而然的将数据融入到 SwiftUI 视图中啦:

// Trip App using modelContainer Scene modifier
import SwiftUI
import SwiftData

@main
struct TripsApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView
        }
        .modelContainer(for: Trip.self)
    }
}

除此之外,借助于 Swift 5.9 中新的宏(Macro)机制我们可以恣意设置和调整 SwiftData 类型中各个属性和关系。比如,我们可以使用 @Transient 修饰符让类中对应的属性不占据数据库的字段空间而是仅存于内存中。

在这里插入图片描述

2. 新的 #Unique 宏

从 iOS 18 开始,苹果为 SwiftData 增加了新的 #Unique 宏用来表示类型中属性的唯一性,并且当新插入的对象与已有对象发生冲突时(Collisions)将新增操作改为数据更新操作:

在这里插入图片描述

在下面的代码中,我们利用 #Unique 宏将 Trip 类中 name、startDate 和 endDate 三个属性的值组合为判断 Trip 在数据库中存在唯一性的判断标准:

// Add unique constraints to avoid duplication
import SwiftData

@Model 
class Trip {
    #Unique<Trip>([\.name, \.startDate, \.endDate])
    
    var name: String
    var destination: String
    var startDate: Date
    var endDate: Date
    
    var bucketList: [BucketListItem] = [BucketListItem]()
    var livingAccommodation: LivingAccommodation?
}

这样一来,借助 #Unique 宏我们即可省去大量附加判断代码,在数据库层面完成托管对象唯一性的检查。

3. 历史数据操作记录

有了上面的 #Unique 宏,我们还可以进一步为 SwiftData 类型的实例属性添加历史(History)记录支持。

在这里插入图片描述

如下代码所示,我们利用 @Attribute 宏在 #Unique 对应的三个属性上开启了 .preserveValueOnDeletion 特性:

// Add .preserveValueOnDeletion to capture unique columns
import SwiftData

@Model 
class Trip {
    #Unique<Trip>([\.name, \.startDate, \.endDate])
    
    @Attribute(.preserveValueOnDeletion)
    var name: String
    var destination: String

    @Attribute(.preserveValueOnDeletion)
    var startDate: Date

    @Attribute(.preserveValueOnDeletion)
    var endDate: Date
    
    var bucketList: [BucketListItem] = [BucketListItem]()
    var livingAccommodation: LivingAccommodation?
}

这样一来我们就将上面三个属性变为了墓碑(tombstone)属性,使他们可以在 Trip 托管对象被删除时在历史记录中得以显现,以便让用户可以直观查看到数据库的“变迁史”。

4. 自定义数据仓库(Data Stores)

在之前的 SwiftData 中我们可以在 SwiftUI 里用 .modelContainer 修改器为特定托管类型创建对应的模型容器:

// Trip App using modelContainer Scene modifier
import SwiftUI
import SwiftData

@main
struct TripsApp: App {   
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Trip.self)
   }
}

我们也可以根据需求创建“心有所想”的复杂模型容器,比如在下面代码中我们创建的 Model Container 容器让所有 Trip 托管对象仅存活于内存中、并开启了自动保存和 Undo 功能:

// Customize a model container in the app
import SwiftUI
import SwiftData

@main
struct TripsApp: App {   
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: Trip.self,
                        inMemory: true,
                        isAutosaveEnabled: true,
                        isUndoEnabled: true)
   }
}

不仅如此,我们还有更加无拘无束创建模型容器的自由:

// Add a model container to the app
import SwiftUI
import SwiftData

@main
struct TripsApp: App {
    var container: ModelContainer = {
        do {
            let configuration = ModelConfiguration(schema: Schema([Trip.self]), url: fileURL)
            return try ModelContainer(for: Trip.self, configurations: configuration)
        }
        catch { ... }
    }()
    
   var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
   }
}

好消息来了!从 iOS 18 开始我们可以更进一步创建自定义数据仓库 Data Stores,这样数据仓库会拥有更加健壮、更加自由的持久后端支持。这是第一次我们可以在 SwiftData 中创建自己的数据存储仓库:

// Use your own custom data store
import SwiftUI
import SwiftData

@main
struct TripsApp: App {
    var container: ModelContainer = {
        do {
            let configuration = JSONStoreConfiguration(schema: Schema([Trip.self]), url: jsonFileURL)
            return try ModelContainer(for: Trip.self, configurations: configuration)
        }
        catch { ... }
    }()
    
   var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
   }
}

想要了解 Data Stores 定制的更多内容,大家可以进一步观赏下图中的 WWDC24 视频课:

在这里插入图片描述

5. Xcode 预览 Traits

大家知道,SwiftData 与 SwiftUI 搭配可谓是“双剑合璧天下无敌”,而 Xcode 预览(Preview)又对 SwiftUI 界面调试有着“匪夷所思”的优秀支持。

在这里插入图片描述

在 iOS 18 中,我们可以让 SwiftData 数据模型在 Xcode 的预览中继续大放异彩。这可以通过预览的 Trait 机制来完成:

// Make preview data using traits

struct SampleData: PreviewModifier {
    static func makeSharedContext() throws -> ModelContainer {
        let config = ModelConfiguration(isStoredInMemoryOnly: true)
        let container = try ModelContainer(for: Trip.self, configurations: config)
        Trip.makeSampleTrips(in: container)
        return container
    }
    
    func body(content: Content, context: ModelContainer) -> some View {
        content.modelContainer(context)
    }
}

extension PreviewTrait where T == Preview.ViewTraits {
    @MainActor static var sampleData: Self = .modifier(SampleData())
}

在上面的代码中,我们让 SampleData 遵循 PreviewModifier 协议并知趣的实现其中两个方法。

这样一来,在 Xcode 预览中使用 SampleData 提供的 SwiftData 数据源就变成“探囊取物”一般的简单啦:

// Use sample data in a preview

import SwiftUI
import SwiftData

struct ContentView: View {
    @Query
    var trips: [Trip]

    var body: some View {
        ...
    }
}

#Preview(traits: .sampleData) {
    ContentView()
}

6. 自定义额外数据 Queries

在 Xcode 预览的调试中,对于某些视图来说其本身并不包含 Query 属性而是需要从外部传入它们。

在这里插入图片描述

从 iOS 18 开始,SwiftData 也为其提供了更“银杏化”的支持。现在我们可以直接在预览 #Preview 附着的闭包中插入所需的 Query 状态了,这是通过 @Previewable 宏来实现的:

// Create a preview query using @Previewable

import SwiftUI
import SwiftData

#Preview(traits: .sampleData) {
    @Previewable @Query var trips: [Trip]
    BucketListItemView(trip: trips.first)
}

有了新的 @Previewable 宏,为特定视图创建 Query 数据集变得史无前例的简单了。

7. #Expression 表达式宏

从 iOS 17 开始,苹果推出了新的 #Predicate 宏让我们可以自由自在的构建数据查询相关的断言(Predicate):

// Create a Predicate to find a Trip based on Search Text

let predicate = #Predicate<Trip> {
    searchText.isEmpty ? true : $0.name.localizedStandardContains(searchText)
}

除了简单的条件以外,我们还可以在 #Predicate 闭包中合成复杂的多条件查询语句:

// Create a Compound Predicate to find a Trip based on Search Text

let predicate = #Predicate<Trip> {
    searchText.isEmpty ? true :
    $0.name.localizedStandardContains(searchText) ||
    $0.destination.localizedStandardContains(searchText)
}

在这里插入图片描述

如今,在 iOS 18+ 中我们的自由度进一步得到与时俱进的增强。现在我们可以使用全新的 #Expression 宏来让断言的表达能力更加“突飞猛进”:

// Build a predicate to find Trips with BucketListItems that are not in the plan

let unplannedItemsExpression = #Expression<[BucketListItem], Int> { items in
    items.filter {
        !$0.isInPlan
    }.count
}

let today = Date.now
let tripsWithUnplannedItems = #Predicate<Trip>{ trip
    // The current date falls within the trip
    (trip.startDate ..< trip.endDate).contains(today) &&

    // The trip has at least one BucketListItem
    // where 'isInPlan' is false
    unplannedItemsExpression.evaluate(trip.bucketList) > 0
}

如上代码所示:我们利用新的 #Expression 宏创建了 unplannedItemsExpression 表达式,并将其应用在了 tripsWithUnplannedItems 断言中从而创建出有史以来最复杂、最富表现力的复合查询条件。

8. #Index 宏

大家都知道,在数据库的查询等操作中为特定字段增加索引可以极大的增加查表速度。

从 iOS 18 开始,数据库索引机制终于显式加入到了 SwiftData 中,这是通过 #Index 宏实现的。

在这里插入图片描述


关于 Apple 数据库相关索引机制的进一步介绍,感兴趣的小伙伴们可以移步如下链接继续观赏精彩的内容:

  • SwiftUI 后台刷新多个 Section 导致 global index in collection view 与实际不匹配问题的解决
  • 探究 CoreData 使用索引(Index)机制加速查表究竟如何实现?

这是一个令我们喜不自胜的 SwiftData 新功能!

使用全新的 #Index 宏我们可以为数据类型中经常访问的特定属性增加查表索引,以便增加查询效率。

如下代码所示,我们不仅可以在单个属性上增加索引,还可以将索引应用到多个属性组合上去。

// Add Index for commonly used KeyPaths or combination of KeyPaths
import SwiftData

@Model 
class Trip {
    #Unique<Trip>([\.name, \.startDate, \.endDate
    #Index<Trip>([\.name], [\.startDate], [\.endDate], [\.name, \.startDate, \.endDate])

    var name: String
    var destination: String
    var startDate: Date
    var endDate: Date
    
    var bucketList: [BucketListItem] = [BucketListItem
    var livingAccommodation: LivingAccommodation
}

为托管类型增加索引,不仅有利于查表速度,对于海量数据的筛选和排序也会大有裨益,棒棒哒!💯

总结

在本篇博文中,我们介绍了 iOS 18 中 SwiftData 框架的“重装升级”。其中我感觉 #Expression 和 #Index 宏对小伙伴的实际帮助更为突出,大家怎么认为呢?欢迎讨论哦。

在这里插入图片描述

感谢观赏,再会!😎

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

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

相关文章

论文中引用网页链接的简单操作

一、参考资料 中文论文或者申请书中网页新闻引用格式 自制网页&#xff1a;在论文中快速引用网页链接 二、相关介绍 1. 常用文献类型用单字母标识 学术论文参考文献中文献类型字母标识 常用文献类型用单字母标识&#xff0c;具体如下&#xff1a; &#xff08;1&#xf…

自动控制理论---线性时不变系统的单位脉冲响应

1、实验设备 PC计算机1台&#xff0c;MATLAB软件1套。 2.实验目的&#xff1a; 学习并理解线性时不变系统的单位脉冲响应的计算方法。掌握MATLAB编程&#xff0c;计算整个系统的单位脉冲响应。 3.实验原理说明&#xff1a; 单位脉冲响应是指在输入信号为单位脉冲序列时&am…

【Java】内部类、枚举、泛型

目录 1.内部类1.1概述1.2分类1.3匿名内部类(重点) 2.枚举2.1一般枚举2.2抽象枚举2.3应用1&#xff1a;用枚举写单例2.4应用2&#xff1a;标识常量 3.泛型3.1泛型认识3.2泛型原理3.3泛型的定义泛型类泛型接口泛型方法 3.4泛型的注意事项 1.内部类 1.1概述 内部类&#xff1a;指…

高分文章发文圣体!一周80%二区以上 | GBD数据库周报(6.05~6.11)

全球疾病负担&#xff08;GBD&#xff09;是迄今为止规模最大、最全面的一项研究&#xff0c;旨在量化不同地区和不同时期的健康损失&#xff0c;从而改善卫生系统并消除差异。 该研究由华盛顿大学健康指标与评估研究所 (IHME) 牵头&#xff0c;是一项真正的全球性研究&#xf…

移动UI:小小工作台,竟然别有洞天(N多样式),上图了。

设计移动UI的工作台页面时&#xff0c;可以考虑以下几个方面&#xff1a; 布局设计&#xff1a;首先确定页面的布局&#xff0c;可以使用单栏、双栏或九宫格等等布局方式。布局应该简洁明了&#xff0c;方便用户快速找到需要的功能模块。功能模块&#xff1a;根据用户需求确定需…

Windows安装配置CUDA12.5

搞大模型往往都需要GPU加速&#xff0c;本次在家里的PC上安装CUDA来实现GPU加速。 一、环境准备 操作系统&#xff1a;Windows11 23H2 GPU&#xff1a;RTX 4070 Ti Super 显卡驱动&#xff1a;555.99 &#xff08;NVIDIA GeForce 驱动程序 - N 卡驱动 | NVIDIA&#xff09; …

从入门到高手的99个python案例(2)

51. 列表和数组比较 - 列表通用&#xff0c;NumPy数组高效。 import numpy as np normal_list [1, 2, 3] np_array np.array([1, 2, 3]) print(np_array.shape) # 输出 (3,), 数组有形状信息 52. Python的内置模块datetime - 处理日期和时间。 from datetime import…

H5拟态个人主页

演示地址&#xff1a;科技语者个人主页 (chgskj.cn) 文末有该项目的源码~ 这张图片的效果你是不是非常想要get同款&#xff1f; 源码就是这个样子 这段HTML代码构建了一个个人主页&#xff0c;结合了CSS样式和JavaScript功能。 下面是对代码的主要组成部分的详细解释&#x…

FPGA - Verilog题目: 非整数倍数据位宽转换24to128

题目描述&#xff1a; 实现数据位宽转换电路&#xff0c;实现24bit数据输入转换为128bit数据输出。其中&#xff0c;先到的数据应置于输出的高bit位。 电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性&#xff0c;valid_out用来指示数据输出data_out的有效性…

建筑电工精选最新模拟试题(含答案)

一、填空题 1、我国安全生产的基本方针是 安全 第一&#xff0c;预防 为主&#xff0c;综合治理。 2、特种作业人员&#xff0c;必须积极主动参加培训与考核 。既是法律法规的规定&#xff0c;也是自身工作&#xff0c;生产及生命安全 的需要 3、触电急救&#x…

QT调用vs2019生成的c++动态库

QT调用vs2019生成的c动态库 dll库的创建方法&#xff1a; VS2019创建c动态链接库dll与调用方法-CSDN博客 加减法示范&#xff1a; 头文件 // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLL3_EXPORTS // 符号编…

植物ATAC-seq文献集锦(三)——果实发育篇

ATAC-seq在植物研究领域的应用我们已经介绍2期了&#xff0c;本期我们聚焦ATAC-seq技术在果实发育方向的应用案例。 植物ATAC-seq文献集锦&#xff08;一&#xff09;——基因组篇 植物ATAC-seq文献集锦&#xff08;二&#xff09;——生长发育篇 文献一&#xff1a;Ident…

Java学习笔记之基本数据类型转换

前言 本篇文章是基于我本人在初学JAVA阶段想记录的的学习笔记&#xff0c;如有错误&#xff0c;恳请指正。今天要干掉的是JAVA的基本数据类型转换 Java的基本数据类型转换 前言一&#xff0c;基本数据类型复习二&#xff0c;基本介绍什么是自动类型转换&#xff1f; 三&#…

如何实现 Python 源码压缩加密常用解决方案详细教程(更新中)

Python是一种高级的、解释型的、面向对象的编程语言&#xff0c;Python 码简洁易读&#xff0c;并且Python语言跨平台&#xff0c;拥有丰富的标准库和第三方库&#xff0c;深受开发人员的喜爱。 Python 程序扩展名 .py&#xff1a;这是 Python 程序的标准文件扩展名。当你创建…

B端颜值无所谓?麻痹自己可以,麻痹业务人员和客户试一试。

很多老铁觉得B端系统颜值和体验无所谓&#xff0c;功能好就行了&#xff0c;我不认同这种说法&#xff0c;我觉得优秀的B端系统应该是内外兼修的&#xff0c;而不是偏科的。你想一想你费尽研发的系统&#xff0c;就是因为颜值问题&#xff0c;你的业务人员没信息推销&#xff0…

用Canvas绘制2D平面近大远小的马路斑马线

用Canvas绘制2D平面近大远小的马路斑马线 设置canvas和上下文&#xff1a; 首先&#xff0c;你需要创建一个元素&#xff0c;并获取其2D渲染上下文。 绘制斑马线&#xff1a; 使用fillRect或strokeRect方法绘制斑马线。你可以通过循环和计算来绘制多条具有不同宽度和间隔的…

【V8引擎】 V8引擎如何运行JS的

文章目录 概要什么是V8引擎为什么需要V8引擎比较常见的javascript引起有哪些呢&#xff1f;V8引擎是如何工作的&#xff08;V8引擎的解析过程&#xff09;V8引擎的做了哪些优化 概要 本篇文章主要是讲V8引擎如何运行JS&#xff0c;对运行JS做了哪些优化 什么是V8引擎 V8 是一…

深度学习 - CNN

第一部分&#xff1a;基础知识 1. 什么是卷积神经网络&#xff08;CNN&#xff09; 定义和基本概念 卷积神经网络&#xff08;CNN&#xff09;是一种专门用于处理具有网格结构数据&#xff08;如图像&#xff09;的深度学习模型。它们在图像识别和计算机视觉领域表现尤为突出…

【操作与配置】Pytorch环境搭建

安装显卡驱动 显卡驱动是一种软件程序&#xff0c;用于控制显卡硬件与操作系统之间的通信和交互。显卡驱动负责向操作系统提供有关显卡硬件的信息&#xff0c;以及使操作系统能够正确地控制和管理显卡的各种功能和性能。显卡驱动还包含了针对不同应用程序和游戏的优化&#xff…

非关系型数据库NoSQL数据层解决方案 之 Mongodb 简介 下载安装 springboot整合与读写操作

MongoDB 简介 MongoDB是一个开源的面向文档的NoSQL数据库&#xff0c;它采用了分布式文件存储的数据结构&#xff0c;是当前非常流行的数据库之一。 以下是MongoDB的主要特点和优势&#xff1a; 面向文档的存储&#xff1a; MongoDB是一个面向文档的数据库管理系统&#xff0…