diff --git a/SwiftPamphletApp.xcodeproj/project.pbxproj b/SwiftPamphletApp.xcodeproj/project.pbxproj index d47d9f51d..ed50e0f47 100644 --- a/SwiftPamphletApp.xcodeproj/project.pbxproj +++ b/SwiftPamphletApp.xcodeproj/project.pbxproj @@ -149,6 +149,7 @@ 08522BE927CF6E3B005FF059 /* SwiftUI Effect(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 08522BE827CF6E3A005FF059 /* SwiftUI Effect(ap).md */; }; 08522BED27CF7A0C005FF059 /* Keyboard(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 08522BEC27CF7A0C005FF059 /* Keyboard(ap).md */; }; 0858C5C72BEBD230004F4C04 /* ContentUnavailableView(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 0858C5C62BEBD230004F4C04 /* ContentUnavailableView(ap).md */; }; + 0858C5C92BECCD17004F4C04 /* SwiftData-资料(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 0858C5C82BECCD17004F4C04 /* SwiftData-资料(ap).md */; }; 085BB77427D22FCA00E8F69A /* SwiftUI动画(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 085BB77327D22FCA00E8F69A /* SwiftUI动画(ap).md */; }; 085BB77627D22FE300E8F69A /* SwiftUI Canvas(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 085BB77527D22FE300E8F69A /* SwiftUI Canvas(ap).md */; }; 08659BC72BE8FD84009B7C00 /* SwiftUI数据流(ap).md in Resources */ = {isa = PBXBuildFile; fileRef = 08659BC62BE8FD84009B7C00 /* SwiftUI数据流(ap).md */; }; @@ -365,6 +366,7 @@ 08522BE827CF6E3A005FF059 /* SwiftUI Effect(ap).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "SwiftUI Effect(ap).md"; sourceTree = ""; }; 08522BEC27CF7A0C005FF059 /* Keyboard(ap).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "Keyboard(ap).md"; sourceTree = ""; }; 0858C5C62BEBD230004F4C04 /* ContentUnavailableView(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "ContentUnavailableView(ap).md"; sourceTree = ""; }; + 0858C5C82BECCD17004F4C04 /* SwiftData-资料(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "SwiftData-资料(ap).md"; sourceTree = ""; }; 085BB77327D22FCA00E8F69A /* SwiftUI动画(ap).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "SwiftUI动画(ap).md"; sourceTree = ""; }; 085BB77527D22FE300E8F69A /* SwiftUI Canvas(ap).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "SwiftUI Canvas(ap).md"; sourceTree = ""; }; 08659BC62BE8FD84009B7C00 /* SwiftUI数据流(ap).md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "SwiftUI数据流(ap).md"; sourceTree = ""; }; @@ -547,12 +549,12 @@ path = macOS; sourceTree = ""; }; - 08448F5C279EB19700B61353 /* Swift三方库使用 */ = { + 08448F5C279EB19700B61353 /* 三方库使用 */ = { isa = PBXGroup; children = ( 08448F5D279EB23600B61353 /* SQLite.swift的使用(ap).md */, ); - path = "Swift三方库使用"; + path = "三方库使用"; sourceTree = ""; }; 08448F67279EB40D00B61353 /* 基础库 */ = { @@ -850,6 +852,7 @@ 08659BD82BE9A80E009B7C00 /* SwiftData多线程(ap).md */, 08659BDA2BE9A834009B7C00 /* SwiftData-版本迁移(ap).md */, 08659BDC2BE9E3AA009B7C00 /* SwiftData-调试(ap).md */, + 0858C5C82BECCD17004F4C04 /* SwiftData-资料(ap).md */, ); path = SwiftData; sourceTree = ""; @@ -994,7 +997,6 @@ 08448F47279E7E3A00B61353 /* SwiftUI */, 08448F48279E7E4100B61353 /* Combine */, 08659BCB2BE9A3E6009B7C00 /* SwiftData */, - 08448F5C279EB19700B61353 /* Swift三方库使用 */, 08026C312869990100792EF1 /* 系统能力 */, 08448F7C279EB6F700B61353 /* 工程模式 */, 08449016279ECD1400B61353 /* Swift Concurrency */, @@ -1003,6 +1005,7 @@ 08026C332869999800792EF1 /* 性能和构建 */, 08448F8D279EB86500B61353 /* 安全 */, 08448F4F279E8E9F00B61353 /* macOS */, + 08448F5C279EB19700B61353 /* 三方库使用 */, 0844902F279ECF7D00B61353 /* 1.md */, ); path = Guide; @@ -1213,6 +1216,7 @@ 08BE634227BFAF76002BC6A8 /* TextEditor(ap).md in Resources */, 08522BDE27CF5133005FF059 /* Stepper(ap).md in Resources */, 08448FB8279EC3C200B61353 /* 属性(ap).md in Resources */, + 0858C5C92BECCD17004F4C04 /* SwiftData-资料(ap).md in Resources */, 0844900F279ECC0C00B61353 /* Combine网络请求(ap).md in Resources */, 08448F9A279EBA2900B61353 /* 可选(ap).md in Resources */, 08026C472869B26900792EF1 /* 调试(ap).md in Resources */, @@ -1530,7 +1534,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_ASSET_PATHS = "\"SwiftPamphletApp/Preview Content\""; DEVELOPMENT_TEAM = 962Z8PV35L; ENABLE_HARDENED_RUNTIME = YES; @@ -1544,7 +1548,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 6.3; + MARKETING_VERSION = 6.3.2; OTHER_LDFLAGS = ( "-Xlinker", "-interposable", @@ -1573,7 +1577,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_ASSET_PATHS = "\"SwiftPamphletApp/Preview Content\""; DEVELOPMENT_TEAM = 962Z8PV35L; ENABLE_HARDENED_RUNTIME = YES; @@ -1587,7 +1591,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 14.0; - MARKETING_VERSION = 6.3; + MARKETING_VERSION = 6.3.2; PRODUCT_BUNDLE_IDENTIFIER = com.starming.SwiftPamphletAppByMing; PRODUCT_NAME = "戴铭的开发小册子"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/SwiftPamphletApp/Guide/GuideListView.swift b/SwiftPamphletApp/Guide/GuideListView.swift index 34d516d9a..0671c6f39 100644 --- a/SwiftPamphletApp/Guide/GuideListView.swift +++ b/SwiftPamphletApp/Guide/GuideListView.swift @@ -234,13 +234,11 @@ final class GuideListModel { L(t: "容器配置modelContainer"), L(t: "增删modelContext"), L(t: "SwiftData-检索"), -// L(t: "SwiftData-处理大量数据"), -// L(t: "SwiftData多线程"), -// L(t: "SwiftData-版本迁移"), -// L(t: "SwiftData-调试") - ]), - L(t: "Swift三方库使用",sub: [ - L(t: "SQLite.swift的使用") + L(t: "SwiftData-处理大量数据"), + L(t: "SwiftData多线程"), + L(t: "SwiftData-版本迁移"), + L(t: "SwiftData-调试"), + L(t: "SwiftData-资料") ]), L(t: "系统能力",sub: [ L(t: "Swift-DocC") @@ -283,6 +281,9 @@ final class GuideListModel { L(t: "全屏模式"), L(t: "macOS共享菜单"), L(t: "macOS剪贴板") + ]), + L(t: "三方库使用",sub: [ + L(t: "SQLite.swift的使用") ]) ] diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\345\244\204\347\220\206\345\244\247\351\207\217\346\225\260\346\215\256(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\345\244\204\347\220\206\345\244\247\351\207\217\346\225\260\346\215\256(ap).md" index 64e53d461..6b437ccfc 100644 --- "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\345\244\204\347\220\206\345\244\247\351\207\217\346\225\260\346\215\256(ap).md" +++ "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\345\244\204\347\220\206\345\244\247\351\207\217\346\225\260\346\215\256(ap).md" @@ -1,2 +1,17 @@ -# <#Title#> +SwiftData 模型上下文有个方法叫 `enumerate()`,可以高效遍历大量数据。 + +```swift +let descriptor = FetchDescriptor
() +... + +do { + try modelContext.enumerate(descriptor, batchSize: 1000) { article in + ... + } +} catch { + print("Failed.") +} +``` + +其中 batchSize 参数是调整批量处理的数量,也就是一次加载多少对象。因此可以通过这个值来权衡内存和IO数量。 diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\347\211\210\346\234\254\350\277\201\347\247\273(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\347\211\210\346\234\254\350\277\201\347\247\273(ap).md" index 64e53d461..8fe969756 100644 --- "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\347\211\210\346\234\254\350\277\201\347\247\273(ap).md" +++ "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\347\211\210\346\234\254\350\277\201\347\247\273(ap).md" @@ -1,2 +1,64 @@ -# <#Title#> +以下的小改动 SwiftData 会自动执行轻量迁移: + +- 增加模型 +- 增加有默认值的新属性 +- 重命名属性 +- 删除属性 +- 增加或删除 `.externalStorage` 或 `.allowsCloudEncryption` 属性。 +- 增加所有值都是唯一属性为 `.unique` +- 调整关系的删除规则 + +其他情况需要用到版本迁移,版本迁移步骤如下: + +- 用 VersionedSchema 创建 SwiftData 模型的版本 +- 用 SchemaMigrationPlan 对创建的版本进行排序 +- 为每个迁移定义一个迁移阶段 + +设置版本 + +```swift +enum ArticleV1Schema: VersionedSchema { + static var versionIdentifier: String? = "v1" + static var models: [any PersistentModel.Type] { [Article.self] } + + @Model + final class Article { + ... + } +} +``` + +SchemaMigrationPlan 轻量迁移 + +```swift +enum ArticleMigrationPlan: SchemaMigrationPlan { + static var schemas: [any VersionedSchema.Type] { + [ArticleV1Schema.self, ArticleV2Schema.self] + } + + static var stages: [MigrationStage] { + [migrateV1toV2] + } + + static let migrateV1toV2 = MigrationStage.lightweight( + fromVersion: ArticleV1Schema.self, + toVersion: ArticleV2Schema.self + ) +} +``` + +自定义迁移 + +```swift +static let migrateV1toV2 = MigrationStage.custom( + fromVersion: ArticleV1Schema.self, + toVersion: ArticleV2Schema.self, + willMigrate: { context in + // 合并前的处理 + }, + didMigrate: { context in + // 合并后的处理 + } +) +``` diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\350\260\203\350\257\225(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\350\260\203\350\257\225(ap).md" index 64e53d461..6b5c1a9b8 100644 --- "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\350\260\203\350\257\225(ap).md" +++ "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\350\260\203\350\257\225(ap).md" @@ -1,2 +1,6 @@ -# <#Title#> +CoreData 的调试方式依然适用于 SwiftData。 + +你可以设置启动参数来让 CoreData 打印出执行的 SQL 语句。在你的项目中,选择 "Product" -> "Scheme" -> "Edit Scheme",然后在 "Arguments" 标签下的 "Arguments Passed On Launch" 中添加 -com.apple.CoreData.SQLDebug 1。这样,每当 CoreData 执行 SQL 语句时,都会在控制台中打印出来。 + +使用 `-com.apple.CoreData.SQLDebug 3` 获取后台更多信息。 diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\350\265\204\346\226\231(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\350\265\204\346\226\231(ap).md" new file mode 100644 index 000000000..ec121af8b --- /dev/null +++ "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData-\350\265\204\346\226\231(ap).md" @@ -0,0 +1,9 @@ + +## WWDC + +23 +- [Dive deeper into SwiftData - WWDC23 - Videos - Apple Developer](https://developer.apple.com/wwdc23/10196) +- [Migrate to SwiftData - WWDC23 - Videos - Apple Developer](https://developer.apple.com/wwdc23/10189) +- [Meet SwiftData - WWDC23 - Videos - Apple Developer](https://developer.apple.com/wwdc23/10187) +- [Model your schema with SwiftData - WWDC23 - Videos - Apple Developer](https://developer.apple.com/wwdc23/10195) +- [Build an app with SwiftData - WWDC23 - Videos - Apple Developer](https://developer.apple.com/wwdc23/10154) diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData\345\244\232\347\272\277\347\250\213(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData\345\244\232\347\272\277\347\250\213(ap).md" index 64e53d461..53f4e1452 100644 --- "a/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData\345\244\232\347\272\277\347\250\213(ap).md" +++ "b/SwiftPamphletApp/Resource/Guide/SwiftData/SwiftData\345\244\232\347\272\277\347\250\213(ap).md" @@ -1,2 +1,27 @@ -# <#Title#> +创建一个 Actor,然后 SwiftData 上下文在其中执行操作。 + +```swift +@ModelActor +actor DataHandler {} + +extension DataHandler { + func addInfo() throws -> IOInfo { + let info = IOInfo() + modelContext.insert(info) + try modelContext.save() + return info + } + ... +} +``` + +使用 + +```swift +Task.detached { + let handler = DataHandler() + let item = try await handler.addInfo() + ... +} +``` diff --git "a/SwiftPamphletApp/Resource/Guide/SwiftData/\345\210\233\345\273\272@Model\346\250\241\345\236\213(ap).md" "b/SwiftPamphletApp/Resource/Guide/SwiftData/\345\210\233\345\273\272@Model\346\250\241\345\236\213(ap).md" index b8750db52..60a8706ed 100644 --- "a/SwiftPamphletApp/Resource/Guide/SwiftData/\345\210\233\345\273\272@Model\346\250\241\345\236\213(ap).md" +++ "b/SwiftPamphletApp/Resource/Guide/SwiftData/\345\210\233\345\273\272@Model\346\250\241\345\236\213(ap).md" @@ -19,6 +19,11 @@ final class Article { } ``` +以下数据类型默认支持: +- 基础类型:Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float, Double, Bool, String, Date, Data 等 +- 复杂的类型:Array, Dictionary, Set, Optional, Enum, Struct, Codable 等 +- 模型关系:一对一、一对多、多对多 + 默认数据库路径: `Data/Library/Application Support/default.store` ## `@Attribute` diff --git "a/SwiftPamphletApp/Resource/Guide/Swift\344\270\211\346\226\271\345\272\223\344\275\277\347\224\250/SQLite.swift\347\232\204\344\275\277\347\224\250(ap).md" "b/SwiftPamphletApp/Resource/Guide/\344\270\211\346\226\271\345\272\223\344\275\277\347\224\250/SQLite.swift\347\232\204\344\275\277\347\224\250(ap).md" similarity index 100% rename from "SwiftPamphletApp/Resource/Guide/Swift\344\270\211\346\226\271\345\272\223\344\275\277\347\224\250/SQLite.swift\347\232\204\344\275\277\347\224\250(ap).md" rename to "SwiftPamphletApp/Resource/Guide/\344\270\211\346\226\271\345\272\223\344\275\277\347\224\250/SQLite.swift\347\232\204\344\275\277\347\224\250(ap).md"