From 105a55fbfb949228e34e838523aa8885ca54915e Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Fri, 9 Jan 2026 12:19:03 -0800 Subject: [PATCH 1/6] wip --- Sources/SQLiteData/CloudKit/SyncEngine.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Sources/SQLiteData/CloudKit/SyncEngine.swift b/Sources/SQLiteData/CloudKit/SyncEngine.swift index 5d094197..69e3fa7b 100644 --- a/Sources/SQLiteData/CloudKit/SyncEngine.swift +++ b/Sources/SQLiteData/CloudKit/SyncEngine.swift @@ -13,6 +13,8 @@ import UIKit #endif +extension String: Error {} + /// An object that manages the synchronization of local and remote SQLite data. /// /// See for more information. @@ -99,10 +101,11 @@ repeat (each T2).PrimaryKey.QueryOutput: IdentifierStringConvertible, repeat (each T2).TableColumns.PrimaryColumn: WritableTableColumnExpression { + @Dependency(\.context) var context let containerIdentifier = containerIdentifier ?? ModelConfiguration(groupContainer: .automatic).cloudKitContainerIdentifier - + (context == .preview ? "preview" : nil) var allTables: [any SynchronizableTable] = [] var allPrivateTables: [any SynchronizableTable] = [] for table in repeat each tables { @@ -113,7 +116,6 @@ } let userDatabase = UserDatabase(database: database) - @Dependency(\.context) var context guard context == .live else { let privateDatabase = MockCloudDatabase(databaseScope: .private) @@ -325,7 +327,10 @@ : URL(filePath: metadatabase.path).lastPathComponent let attachedMetadatabaseName = URL(string: attachedMetadatabasePath)?.lastPathComponent ?? "" - if metadatabaseName != attachedMetadatabaseName { + @Dependency(\.context) var context + if metadatabaseName != attachedMetadatabaseName + && !(context == .preview && attachedMetadatabaseName.isEmpty) + { throw SchemaError( reason: .metadatabaseMismatch( attachedPath: attachedMetadatabasePath, @@ -337,7 +342,6 @@ """ ) } - } else { try #sql( """ @@ -2151,9 +2155,11 @@ /// - Parameter containerIdentifier: The identifier of the CloudKit container used to /// synchronize data. Defaults to the value set in the app's entitlements. public func attachMetadatabase(containerIdentifier: String? = nil) throws { + @Dependency(\.context) var context let containerIdentifier = containerIdentifier ?? ModelConfiguration(groupContainer: .automatic).cloudKitContainerIdentifier + ?? (context == .preview ? "preview" : nil) guard let containerIdentifier else { throw SyncEngine.SchemaError.noCloudKitContainer From 3969174f632e6ce1d08d9e734af3190c89035c62 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Fri, 9 Jan 2026 12:19:26 -0800 Subject: [PATCH 2/6] wip --- Examples/CloudKitDemo/CloudKitDemoApp.swift | 7 ++- .../CloudKitDemo/CountersListFeature.swift | 10 +++- Examples/CloudKitDemo/MyLibrary/.gitignore | 8 +++ Examples/CloudKitDemo/MyLibrary/Package.swift | 35 +++++++++++++ .../Sources/MyLibrary/MyLibrary.swift | 51 +++++++++++++++++++ .../MyLibrary/Sources/MyLibrary/Schema.swift | 7 +++ .../Tests/MyLibraryTests/MyLibraryTests.swift | 6 +++ Examples/CloudKitDemo/Schema.swift | 6 ++- .../xcshareddata/swiftpm/Package.resolved | 20 +------- 9 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 Examples/CloudKitDemo/MyLibrary/.gitignore create mode 100644 Examples/CloudKitDemo/MyLibrary/Package.swift create mode 100644 Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift create mode 100644 Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift create mode 100644 Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift diff --git a/Examples/CloudKitDemo/CloudKitDemoApp.swift b/Examples/CloudKitDemo/CloudKitDemoApp.swift index aef2e449..e29700d3 100644 --- a/Examples/CloudKitDemo/CloudKitDemoApp.swift +++ b/Examples/CloudKitDemo/CloudKitDemoApp.swift @@ -5,10 +5,13 @@ import SwiftUI @main struct CloudKitDemoApp: App { @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate + @Dependency(\.context) var context init() { - try! prepareDependencies { - try $0.bootstrapDatabase() + if context == .live { + try! prepareDependencies { + try $0.bootstrapDatabase() + } } } diff --git a/Examples/CloudKitDemo/CountersListFeature.swift b/Examples/CloudKitDemo/CountersListFeature.swift index 98b904e2..1a1a5c6f 100644 --- a/Examples/CloudKitDemo/CountersListFeature.swift +++ b/Examples/CloudKitDemo/CountersListFeature.swift @@ -1,7 +1,6 @@ import CloudKit import SQLiteData import SwiftUI -import SwiftUINavigation struct CountersListView: View { @FetchAll var counters: [Counter] @@ -107,3 +106,12 @@ struct CounterRow: View { } } } + +#Preview { + let _ = try! prepareDependencies { + try $0.bootstrapDatabase() + } + NavigationStack { + CountersListView() + } +} diff --git a/Examples/CloudKitDemo/MyLibrary/.gitignore b/Examples/CloudKitDemo/MyLibrary/.gitignore new file mode 100644 index 00000000..0023a534 --- /dev/null +++ b/Examples/CloudKitDemo/MyLibrary/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Examples/CloudKitDemo/MyLibrary/Package.swift b/Examples/CloudKitDemo/MyLibrary/Package.swift new file mode 100644 index 00000000..48576ee7 --- /dev/null +++ b/Examples/CloudKitDemo/MyLibrary/Package.swift @@ -0,0 +1,35 @@ +// swift-tools-version: 6.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyLibrary", + platforms: [ + .iOS(.v26), + ], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "MyLibrary", + targets: ["MyLibrary"] + ), + ], + dependencies: [ + .package(path: "../../..") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "MyLibrary", + dependencies: [ + .product(name: "SQLiteData", package: "sqlite-data") + ] + ), + .testTarget( + name: "MyLibraryTests", + dependencies: ["MyLibrary"] + ), + ] +) diff --git a/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift b/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift new file mode 100644 index 00000000..862147c1 --- /dev/null +++ b/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift @@ -0,0 +1,51 @@ +import Dependencies +import Foundation +import GRDB +import SQLiteData + +//@Table +//nonisolated struct Counter: Identifiable { +// let id: UUID +// var count = 0 +//} + +extension DependencyValues { + mutating func bootstrapDatabase() throws { + var configuration = Configuration() +// configuration.prepareDatabase { db in +// try db.attachMetadatabase() +// } +// let database = try SQLiteData.defaultDatabase(configuration: configuration) +// +// var migrator = DatabaseMigrator() +// #if DEBUG +// migrator.eraseDatabaseOnSchemaChange = true +// #endif +// migrator.registerMigration("Create tables") { db in +// try #sql( +// """ +// CREATE TABLE "counters" ( +// "id" TEXT PRIMARY KEY NOT NULL ON CONFLICT REPLACE DEFAULT (uuid()), +// "count" INT NOT NULL ON CONFLICT REPLACE DEFAULT 0 +// ) STRICT +// """ +// ) +// .execute(db) +// } +// try migrator.migrate(database) +// defaultDatabase = database +// defaultSyncEngine = try SyncEngine( +// for: defaultDatabase, +// tables: Counter.self +// ) + } +} + +import SwiftUI + +#Preview { +// let _ = try! prepareDependencies { +// try $0.bootstrapDatabase() +// } + Text("Ok") +} diff --git a/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift b/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift new file mode 100644 index 00000000..0ef31ffc --- /dev/null +++ b/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift @@ -0,0 +1,7 @@ +// +// Schema.swift +// MyLibrary +// +// Created by Stephen Celis on 1/9/26. +// + diff --git a/Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift b/Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift new file mode 100644 index 00000000..d6d43bfa --- /dev/null +++ b/Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift @@ -0,0 +1,6 @@ +import Testing +@testable import MyLibrary + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/Examples/CloudKitDemo/Schema.swift b/Examples/CloudKitDemo/Schema.swift index 4721e583..02a8c947 100644 --- a/Examples/CloudKitDemo/Schema.swift +++ b/Examples/CloudKitDemo/Schema.swift @@ -11,7 +11,11 @@ nonisolated struct Counter: Identifiable { extension DependencyValues { mutating func bootstrapDatabase() throws { @Dependency(\.context) var context - let database = try SQLiteData.defaultDatabase() + var configuration = Configuration() + configuration.prepareDatabase { db in + try db.attachMetadatabase() + } + let database = try SQLiteData.defaultDatabase(configuration: configuration) logger.debug( """ App database diff --git a/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4ff76b6f..1f540899 100644 --- a/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Examples/Examples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "41e7781e6c506773b6af84af513bcd6d3b1be59d635e6c4c4bd89638368e4629", + "originHash" : "c133bf7d10c8ce1e5d6506c3d2f080eac8b4c8c2827044d53a9b925e903564fd", "pins" : [ { "identity" : "combine-schedulers", @@ -73,24 +73,6 @@ "version" : "1.10.0" } }, - { - "identity" : "swift-docc-plugin", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-docc-plugin", - "state" : { - "revision" : "3e4f133a77e644a5812911a0513aeb7288b07d06", - "version" : "1.4.5" - } - }, - { - "identity" : "swift-docc-symbolkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftlang/swift-docc-symbolkit", - "state" : { - "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", - "version" : "1.0.0" - } - }, { "identity" : "swift-identified-collections", "kind" : "remoteSourceControl", From 03a6ff3e76254ef319138bf09cd58783cd9aee47 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Fri, 9 Jan 2026 12:25:18 -0800 Subject: [PATCH 3/6] wip --- Examples/CloudKitDemo/MyLibrary/.gitignore | 8 --- Examples/CloudKitDemo/MyLibrary/Package.swift | 35 ------------- .../Sources/MyLibrary/MyLibrary.swift | 51 ------------------- .../MyLibrary/Sources/MyLibrary/Schema.swift | 7 --- .../Tests/MyLibraryTests/MyLibraryTests.swift | 6 --- 5 files changed, 107 deletions(-) delete mode 100644 Examples/CloudKitDemo/MyLibrary/.gitignore delete mode 100644 Examples/CloudKitDemo/MyLibrary/Package.swift delete mode 100644 Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift delete mode 100644 Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift delete mode 100644 Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift diff --git a/Examples/CloudKitDemo/MyLibrary/.gitignore b/Examples/CloudKitDemo/MyLibrary/.gitignore deleted file mode 100644 index 0023a534..00000000 --- a/Examples/CloudKitDemo/MyLibrary/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -.DS_Store -/.build -/Packages -xcuserdata/ -DerivedData/ -.swiftpm/configuration/registries.json -.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata -.netrc diff --git a/Examples/CloudKitDemo/MyLibrary/Package.swift b/Examples/CloudKitDemo/MyLibrary/Package.swift deleted file mode 100644 index 48576ee7..00000000 --- a/Examples/CloudKitDemo/MyLibrary/Package.swift +++ /dev/null @@ -1,35 +0,0 @@ -// swift-tools-version: 6.2 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -import PackageDescription - -let package = Package( - name: "MyLibrary", - platforms: [ - .iOS(.v26), - ], - products: [ - // Products define the executables and libraries a package produces, making them visible to other packages. - .library( - name: "MyLibrary", - targets: ["MyLibrary"] - ), - ], - dependencies: [ - .package(path: "../../..") - ], - targets: [ - // Targets are the basic building blocks of a package, defining a module or a test suite. - // Targets can depend on other targets in this package and products from dependencies. - .target( - name: "MyLibrary", - dependencies: [ - .product(name: "SQLiteData", package: "sqlite-data") - ] - ), - .testTarget( - name: "MyLibraryTests", - dependencies: ["MyLibrary"] - ), - ] -) diff --git a/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift b/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift deleted file mode 100644 index 862147c1..00000000 --- a/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/MyLibrary.swift +++ /dev/null @@ -1,51 +0,0 @@ -import Dependencies -import Foundation -import GRDB -import SQLiteData - -//@Table -//nonisolated struct Counter: Identifiable { -// let id: UUID -// var count = 0 -//} - -extension DependencyValues { - mutating func bootstrapDatabase() throws { - var configuration = Configuration() -// configuration.prepareDatabase { db in -// try db.attachMetadatabase() -// } -// let database = try SQLiteData.defaultDatabase(configuration: configuration) -// -// var migrator = DatabaseMigrator() -// #if DEBUG -// migrator.eraseDatabaseOnSchemaChange = true -// #endif -// migrator.registerMigration("Create tables") { db in -// try #sql( -// """ -// CREATE TABLE "counters" ( -// "id" TEXT PRIMARY KEY NOT NULL ON CONFLICT REPLACE DEFAULT (uuid()), -// "count" INT NOT NULL ON CONFLICT REPLACE DEFAULT 0 -// ) STRICT -// """ -// ) -// .execute(db) -// } -// try migrator.migrate(database) -// defaultDatabase = database -// defaultSyncEngine = try SyncEngine( -// for: defaultDatabase, -// tables: Counter.self -// ) - } -} - -import SwiftUI - -#Preview { -// let _ = try! prepareDependencies { -// try $0.bootstrapDatabase() -// } - Text("Ok") -} diff --git a/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift b/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift deleted file mode 100644 index 0ef31ffc..00000000 --- a/Examples/CloudKitDemo/MyLibrary/Sources/MyLibrary/Schema.swift +++ /dev/null @@ -1,7 +0,0 @@ -// -// Schema.swift -// MyLibrary -// -// Created by Stephen Celis on 1/9/26. -// - diff --git a/Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift b/Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift deleted file mode 100644 index d6d43bfa..00000000 --- a/Examples/CloudKitDemo/MyLibrary/Tests/MyLibraryTests/MyLibraryTests.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Testing -@testable import MyLibrary - -@Test func example() async throws { - // Write your test here and use APIs like `#expect(...)` to check expected conditions. -} From bef70646ecacf7c4e25dae9a6bad4d9e7828be13 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Fri, 9 Jan 2026 12:26:54 -0800 Subject: [PATCH 4/6] wip --- Sources/SQLiteData/CloudKit/SyncEngine.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Sources/SQLiteData/CloudKit/SyncEngine.swift b/Sources/SQLiteData/CloudKit/SyncEngine.swift index 69e3fa7b..24891aa4 100644 --- a/Sources/SQLiteData/CloudKit/SyncEngine.swift +++ b/Sources/SQLiteData/CloudKit/SyncEngine.swift @@ -13,8 +13,6 @@ import UIKit #endif -extension String: Error {} - /// An object that manages the synchronization of local and remote SQLite data. /// /// See for more information. @@ -105,7 +103,7 @@ extension String: Error {} let containerIdentifier = containerIdentifier ?? ModelConfiguration(groupContainer: .automatic).cloudKitContainerIdentifier - (context == .preview ? "preview" : nil) + ?? (context == .preview ? "preview" : nil) var allTables: [any SynchronizableTable] = [] var allPrivateTables: [any SynchronizableTable] = [] for table in repeat each tables { From a6cc565bf1ecfe72525c6fd2a445c2c5eb5d897e Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Fri, 9 Jan 2026 12:28:10 -0800 Subject: [PATCH 5/6] wip --- Examples/CloudKitDemo/Schema.swift | 1 - Examples/SyncUps/Schema.swift | 1 - 2 files changed, 2 deletions(-) diff --git a/Examples/CloudKitDemo/Schema.swift b/Examples/CloudKitDemo/Schema.swift index 02a8c947..03eaf63c 100644 --- a/Examples/CloudKitDemo/Schema.swift +++ b/Examples/CloudKitDemo/Schema.swift @@ -10,7 +10,6 @@ nonisolated struct Counter: Identifiable { extension DependencyValues { mutating func bootstrapDatabase() throws { - @Dependency(\.context) var context var configuration = Configuration() configuration.prepareDatabase { db in try db.attachMetadatabase() diff --git a/Examples/SyncUps/Schema.swift b/Examples/SyncUps/Schema.swift index 9ac636d2..a3063f5b 100644 --- a/Examples/SyncUps/Schema.swift +++ b/Examples/SyncUps/Schema.swift @@ -77,7 +77,6 @@ extension Int { extension DependencyValues { mutating func bootstrapDatabase() throws { - @Dependency(\.context) var context let database = try SQLiteData.defaultDatabase() logger.debug( """ From 069e05e8703159e244343aa2fc37d20233f151f1 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Fri, 9 Jan 2026 15:17:37 -0800 Subject: [PATCH 6/6] Conditionally wrap CountersListView in NavigationStack --- Examples/CloudKitDemo/CloudKitDemoApp.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Examples/CloudKitDemo/CloudKitDemoApp.swift b/Examples/CloudKitDemo/CloudKitDemoApp.swift index e29700d3..d02d1d73 100644 --- a/Examples/CloudKitDemo/CloudKitDemoApp.swift +++ b/Examples/CloudKitDemo/CloudKitDemoApp.swift @@ -17,8 +17,10 @@ struct CloudKitDemoApp: App { var body: some Scene { WindowGroup { - NavigationStack { - CountersListView() + if context == .live { + NavigationStack { + CountersListView() + } } } }