Token导航 LogoToken导航TokenDH.com
开发只读github未标认证来源可访问许可证需确认审计通过

swiftdata-persistence快速数据持久化

Agent Skill

用于辅助数据整理、表格处理、CSV/Excel 分析、指标计算和图表准备。它适合让 Agent 清洗字段、汇总数据、发现异常、生成统计口径或把分析结果转成可读说明。使用时需要确认数据来源、字段含义和时间范围,避免把样本数据当全量事实;涉及敏感数据、导出文件或批量写回时,应先确认权限和脱敏边界。

总安装

222

周安装

9

GitHub Stars

1

下载量

70
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:swiftdata-persistence(快速数据持久化)
来源仓库:https://github.com/bluewaves-creations/bluewaves-skills
仓库路径:skills/swiftdata-persistence
安装命令:
npx skills add https://github.com/bluewaves-creations/bluewaves-skills --skill swiftdata-persistence
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/bluewaves-creations/bluewaves-skills --skill swiftdata-persistence

简介

swiftdata-persistence 用于辅助数据整理、表格处理、CSV/Excel 分析、指标计算和图表准备。

  • 适合让 Agent 清洗字段、汇总数据、发现异常、生成统计口径或把分析结果转成可读说明。
  • 使用时需要确认数据来源、字段含义和时间范围,避免把样本数据当全量事实。
  • 涉及敏感数据、导出文件或批量写回时,应先确认权限和脱敏边界。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装并使用。

SKILL.md

SwiftData Persistence

Comprehensive guide to SwiftData framework, the @Model macro, reactive queries, relationships, and native iCloud synchronization for iOS 26 development.

Prerequisites

  • iOS 17+ for SwiftData (iOS 26 recommended)
  • Xcode 26+

@Model Macro Basics

Defining a Model

import SwiftData

@Model
class Note {
    var title: String
    var content: String
    var createdAt: Date
    var isPinned: Bool

    init(title: String, content: String = "") {
        self.title = title
        self.content = content
        self.createdAt = Date()
        self.isPinned = false
    }
}

What @Model Provides

The @Model macro automatically:

  • Makes the class persistable
  • Tracks property changes
  • Enables SwiftUI observation
  • Generates schema metadata

Model Requirements

@Model
class Item {
    // All stored properties must be:
    // - Codable types (String, Int, Date, Data, etc.)
    // - Other @Model types (relationships)
    // - Arrays/optionals of the above

    var name: String           // ✓ Codable
    var count: Int             // ✓ Codable
    var timestamp: Date        // ✓ Codable
    var data: Data             // ✓ Codable
    var tags: [String]         // ✓ Array of Codable
    var metadata: [String: String]  // ✓ Dictionary of Codable
    var related: RelatedItem?  // ✓ Optional @Model relationship

    // Computed properties are NOT persisted
    var displayName: String {
        name.uppercased()
    }

    init(name: String) {
        self.name = name
        self.count = 0
        self.timestamp = Date()
        self.data = Data()
        self.tags = []
    }
}

Model Attributes

@Attribute Macro

@Model
class User {
    // Unique constraint (NOT compatible with iCloud sync)
    @Attribute(.unique)
    var email: String

    // Spotlight indexing
    @Attribute(.spotlight)
    var name: String

    // External storage for large data
    @Attribute(.externalStorage)
    var profileImage: Data?

    // Encryption (device-only, not synced to iCloud)
    @Attribute(.encrypt)
    var sensitiveData: String?

    // Preserve value when nil assigned
    @Attribute(.preserveValueOnDeletion)
    var archiveReason: String?

    // Ephemeral (not persisted)
    @Attribute(.ephemeral)
    var temporaryState: String?

    // Custom original name for migration
    @Attribute(originalName: "userName")
    var displayName: String

    init(email: String, name: String) {
        self.email = email
        self.name = name
        self.displayName = name
    }
}

@Transient Macro

@Model
class Document {
    var title: String
    var content: String

    // Not persisted, recalculated
    @Transient
    var wordCount: Int = 0

    init(title: String, content: String) {
        self.title = title
        self.content = content
        self.wordCount = content.split(separator: " ").count
    }
}

Relationships

One-to-Many Relationship

@Model
class Folder {
    var name: String

    // One folder has many notes
    @Relationship(deleteRule: .cascade)
    var notes: [Note] = []

    init(name: String) {
        self.name = name
    }
}

@Model
class Note {
    var title: String
    var content: String

    // Many notes belong to one folder (inverse)
    var folder: Folder?

    init(title: String, content: String = "", folder: Folder? = nil) {
        self.title = title
        self.content = content
        self.folder = folder
    }
}

Many-to-Many Relationship

@Model
class Note {
    var title: String

    // Note can have many tags
    @Relationship(inverse: \Tag.notes)
    var tags: [Tag] = []

    init(title: String) {
        self.title = title
    }
}

@Model
class Tag {
    var name: String

    // Tag can be on many notes
    var notes: [Note] = []

    init(name: String) {
        self.name = name
    }
}

Delete Rules

@Relationship(deleteRule: .cascade)   // Delete related objects
@Relationship(deleteRule: .nullify)   // Set relationship to nil (default)
@Relationship(deleteRule: .deny)      // Prevent deletion if related exist
@Relationship(deleteRule: .noAction)  // Do nothing

iCloud-Compatible Relationships

Important: For iCloud sync, all relationships MUST be optional:

@Model
class Note {
    var title: String

    // REQUIRED for iCloud: Optional relationships
    var folder: Folder?
    var tags: [Tag]?  // Optional array

    init(title: String) {
        self.title = title
    }
}

ModelContainer Configuration

Basic Setup

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: [Note.self, Folder.self, Tag.self])
    }
}

Custom Configuration

@main
struct MyApp: App {
    let container: ModelContainer

    init() {
        let schema = Schema([Note.self, Folder.self, Tag.self])

        let config = ModelConfiguration(
            schema: schema,
            isStoredInMemoryOnly: false,
            allowsSave: true
        )

        do {
            container = try ModelContainer(for: schema, configurations: config)
        } catch {
            fatalError("Failed to configure SwiftData: \(error)")
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
}

Multiple Configurations

let userConfig = ModelConfiguration(
    "UserData",
    schema: Schema([User.self]),
    url: userDataURL
)

let cacheConfig = ModelConfiguration(
    "Cache",
    schema: Schema([CachedItem.self]),
    isStoredInMemoryOnly: true
)

let container = try ModelContainer(
    for: Schema([User.self, CachedItem.self]),
    configurations: userConfig, cacheConfig
)

Native iCloud Sync

Enabling iCloud Sync (One Line!)

SwiftData includes native iCloud sync - no CloudKit code required:

@main
struct MyApp: App {
    let container: ModelContainer

    init() {
        let schema = Schema([Note.self, Tag.self])

        let config = ModelConfiguration(
            schema: schema,
            cloudKitDatabase: .automatic  // That's it!
        )

        do {
            container = try ModelContainer(for: schema, configurations: config)
        } catch {
            fatalError("Failed to configure SwiftData: \(error)")
        }
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(container)
    }
}

cloudKitDatabase Options

// Automatic iCloud sync (recommended)
cloudKitDatabase: .automatic

// Specific CloudKit container
cloudKitDatabase: .private("iCloud.com.yourcompany.yourapp")

// No iCloud sync (local only)
cloudKitDatabase: .none

Xcode Setup for iCloud

  1. Select your target in Xcode
  2. Go to "Signing & Capabilities"
  3. Click "+ Capability"
  4. Add "iCloud"
  5. Check "CloudKit"
  6. Select or create a CloudKit container
  7. Add "Background Modes" capability
  8. Check "Remote notifications"

iCloud-Compatible Model Requirements

Critical rules for iCloud sync:

@Model
class Note {
    // ✓ Default values for non-optional properties
    var title: String = ""
    var content: String = ""
    var createdAt: Date = Date()

    // ✓ Optional relationships
    var folder: Folder?
    var tags: [Tag]?

    // ✗ NO unique constraints (not supported by CloudKit)
    // @Attribute(.unique) var id: String  // DON'T DO THIS

    // ✗ NO deny delete rules
    // @Relationship(deleteRule: .deny)    // DON'T DO THIS

    init(title: String = "", content: String = "") {
        self.title = title
        self.content = content
        self.createdAt = Date()
    }
}

Schema Migration for iCloud

After shipping to production:

// DO:
// - Add new optional properties with defaults
// - Add new optional relationships

// DON'T:
// - Delete properties (data loss)
// - Rename properties (treated as delete + add)
// - Change property types
// - Add required properties without defaults

Initialize CloudKit Schema

Before first production release:

#if DEBUG
// Run once to create CloudKit schema
try container.mainContext.initializeCloudKitSchema()
#endif

@Query Macro

Basic Query

struct NotesListView: View {
    @Query var notes: [Note]

    var body: some View {
        List(notes) { note in
            Text(note.title)
        }
    }
}

Sorted Query

// Single sort
@Query(sort: \Note.createdAt, order: .reverse)
var notes: [Note]

// Multiple sorts
@Query(sort: [
    SortDescriptor(\Note.isPinned, order: .reverse),
    SortDescriptor(\Note.createdAt, order: .reverse)
])
var notes: [Note]

Filtered Query

// Static predicate
@Query(filter: #Predicate<Note> { note in
    note.isPinned == true
})
var pinnedNotes: [Note]

// Complex predicate
@Query(filter: #Predicate<Note> { note in
    note.title.contains("Swift") && !note.content.isEmpty
})
var swiftNotes: [Note]

Dynamic Filtering

struct SearchableNotesView: View {
    @State private var searchText = ""

    var body: some View {
        FilteredNotesView(searchText: searchText)
            .searchable(text: $searchText)
    }
}

struct FilteredNotesView: View {
    @Query var notes: [Note]

    init(searchText: String) {
        let predicate = #Predicate<Note> { note in
            searchText.isEmpty || note.title.localizedStandardContains(searchText)
        }
        _notes = Query(filter: predicate, sort: \.createdAt, order: .reverse)
    }

    var body: some View {
        List(notes) { note in
            Text(note.title)
        }
    }
}

Query with Limit

@Query(sort: \Note.createdAt, order: .reverse)
var recentNotes: [Note]

// In view, limit manually
List(recentNotes.prefix(10)) { note in
    Text(note.title)
}

Query Animations

@Query(sort: \Note.title, animation: .default)
var notes: [Note]

ModelContext Operations

Accessing Context

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

    // ...
}

Creating Objects

func createNote() {
    let note = Note(title: "New Note")
    modelContext.insert(note)
    // Auto-saved on SwiftUI lifecycle events
}

Explicit Save

func saveChanges() {
    do {
        try modelContext.save()
    } catch {
        print("Save failed: \(error)")
    }
}

Deleting Objects

func deleteNote(_ note: Note) {
    modelContext.delete(note)
}

func deleteNotes(at offsets: IndexSet) {
    for index in offsets {
        modelContext.delete(notes[index])
    }
}

Fetching with Descriptor

func fetchRecentNotes() throws -> [Note] {
    let descriptor = FetchDescriptor<Note>(
        predicate: #Predicate { $0.isPinned },
        sortBy: [SortDescriptor(\.createdAt, order: .reverse)]
    )
    return try modelContext.fetch(descriptor)
}

// With limit
func fetchTopNotes(limit: Int) throws -> [Note] {
    var descriptor = FetchDescriptor<Note>(
        sortBy: [SortDescriptor(\.createdAt, order: .reverse)]
    )
    descriptor.fetchLimit = limit
    return try modelContext.fetch(descriptor)
}

Batch Operations

// Delete all matching predicate
try modelContext.delete(model: Note.self, where: #Predicate { note in
    note.createdAt < cutoffDate
})

// Enumerate for batch processing
let descriptor = FetchDescriptor<Note>()
try modelContext.enumerate(descriptor) { note in
    note.processedAt = Date()
}

#Predicate Macro

Basic Predicates

// Equality
#Predicate<Note> { $0.isPinned == true }

// Comparison
#Predicate<Note> { $0.createdAt > someDate }

// String contains
#Predicate<Note> { $0.title.contains("Swift") }

// Case-insensitive contains
#Predicate<Note> { $0.title.localizedStandardContains(searchText) }

Compound Predicates

// AND
#Predicate<Note> { note in
    note.isPinned && note.title.contains("Important")
}

// OR
#Predicate<Note> { note in
    note.isPinned || note.folder?.name == "Favorites"
}

// NOT
#Predicate<Note> { note in
    !note.content.isEmpty
}

Optional Handling

#Predicate<Note> { note in
    note.folder?.name == "Work"
}

// Check for nil
#Predicate<Note> { note in
    note.folder != nil
}

Array Predicates

// Array contains
#Predicate<Note> { note in
    note.tags?.contains(where: { $0.name == "Important" }) ?? false
}

// Array is empty
#Predicate<Note> { note in
    note.tags?.isEmpty ?? true
}

Model Inheritance (iOS 26)

Base and Derived Models

@Model
class MediaItem {
    var title: String
    var createdAt: Date

    init(title: String) {
        self.title = title
        self.createdAt = Date()
    }
}

@Model
final class Photo: MediaItem {
    var imageData: Data?
    var resolution: String?

    init(title: String, imageData: Data?) {
        super.init(title: title)
        self.imageData = imageData
    }
}

@Model
final class Video: MediaItem {
    var duration: TimeInterval
    var thumbnailData: Data?

    init(title: String, duration: TimeInterval) {
        super.init(title: title)
        self.duration = duration
    }
}

Polymorphic Queries

// Query all media items (photos and videos)
@Query var allMedia: [MediaItem]

// Query only photos
@Query var photos: [Photo]

Background Operations

Background Context

func importData() async {
    let container = modelContainer

    await Task.detached {
        let context = ModelContext(container)

        // Perform operations
        for item in largeDataSet {
            let note = Note(title: item.title)
            context.insert(note)
        }

        try? context.save()
    }.value
}

Actor Isolation

@ModelActor
actor DataImporter {
    func importNotes(from data: [ImportData]) throws {
        for item in data {
            let note = Note(title: item.title, content: item.content)
            modelContext.insert(note)
        }
        try modelContext.save()
    }
}

// Usage
let importer = DataImporter(modelContainer: container)
try await importer.importNotes(from: importData)

Migration

Lightweight Migration (Automatic)

SwiftData handles lightweight migrations automatically:

  • Adding new properties with defaults
  • Removing properties
  • Adding optional relationships

Custom Migration

enum MySchemaV1: VersionedSchema {
    static var versionIdentifier = Schema.Version(1, 0, 0)

    static var models: [any PersistentModel.Type] {
        [Note.self]
    }

    @Model
    class Note {
        var title: String
        var content: String

        init(title: String, content: String) {
            self.title = title
            self.content = content
        }
    }
}

enum MySchemaV2: VersionedSchema {
    static var versionIdentifier = Schema.Version(2, 0, 0)

    static var models: [any PersistentModel.Type] {
        [Note.self]
    }

    @Model
    class Note {
        var title: String
        var content: String
        var createdAt: Date  // New property

        init(title: String, content: String) {
            self.title = title
            self.content = content
            self.createdAt = Date()
        }
    }
}

enum MyMigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [MySchemaV1.self, MySchemaV2.self]
    }

    static var stages: [MigrationStage] {
        [migrateV1toV2]
    }

    static let migrateV1toV2 = MigrationStage.lightweight(
        fromVersion: MySchemaV1.self,
        toVersion: MySchemaV2.self
    )
}

// Use in container
let container = try ModelContainer(
    for: Note.self,
    migrationPlan: MyMigrationPlan.self
)

Testing

In-Memory Testing

import Testing
import SwiftData

@Test
func testNoteCreation() throws {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try ModelContainer(for: Note.self, configurations: config)
    let context = ModelContext(container)

    let note = Note(title: "Test", content: "Content")
    context.insert(note)

    let descriptor = FetchDescriptor<Note>()
    let notes = try context.fetch(descriptor)

    #expect(notes.count == 1)
    #expect(notes.first?.title == "Test")
}

SwiftUI Preview with Sample Data

@MainActor
let previewContainer: ModelContainer = {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: Note.self, configurations: config)

    // Insert sample data
    let context = container.mainContext
    let sampleNotes = [
        Note(title: "First Note", content: "Content 1"),
        Note(title: "Second Note", content: "Content 2")
    ]
    sampleNotes.forEach { context.insert($0) }

    return container
}()

#Preview {
    NotesListView()
        .modelContainer(previewContainer)
}

Best Practices

1. Design for iCloud from the Start

// GOOD: iCloud-compatible model
@Model
class Note {
    var title: String = ""
    var content: String = ""
    var folder: Folder?      // Optional relationship
    var tags: [Tag]?         // Optional array

    init(title: String = "") {
        self.title = title
    }
}

// AVOID: iCloud-incompatible
@Model
class Note {
    @Attribute(.unique) var id: String  // Not supported
    var folder: Folder                   // Non-optional relationship
}

2. Use @Query for Reactive Data

// GOOD: Reactive updates
@Query(sort: \Note.createdAt)
var notes: [Note]

// AVOID: Manual fetching in views
@State private var notes: [Note] = []
func loadNotes() {
    notes = try? context.fetch(FetchDescriptor<Note>())
}

3. Explicit Save for Critical Data

func saveImportantChange() {
    modelContext.insert(criticalData)
    do {
        try modelContext.save()
    } catch {
        // Handle error appropriately
    }
}

4. Use Background Contexts for Heavy Work

func importLargeDataset() async {
    await Task.detached {
        let context = ModelContext(container)
        // Heavy operations
        try? context.save()
    }.value
}

Official Resources

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

38.78%
按下载量换算27

Claude

31.56%
按下载量换算22

Cursor

16.89%
按下载量换算12

Gemini CLI

8.67%
按下载量换算6

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

只读

该 Skill 主要提供规则、说明或参考内容,本身偏只读;真正读写文件、联网或执行命令仍取决于宿主 Agent 的任务。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills