Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Examples/Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,7 @@
DEVELOPMENT_TEAM = VFRXY8HC3H;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Reminders/Info.plist;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down Expand Up @@ -860,6 +861,7 @@
DEVELOPMENT_TEAM = VFRXY8HC3H;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Reminders/Info.plist;
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
Expand Down
36 changes: 28 additions & 8 deletions Examples/Reminders/RemindersLists.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class RemindersListsModel {
RemindersList
.group(by: \.id)
.order(by: \.position)
.leftJoin(Reminder.all) { $0.id.eq($1.remindersListID) && !$1.isCompleted
.leftJoin(Reminder.all) {
$0.id.eq($1.remindersListID) && !$1.isCompleted
}
.leftJoin(SyncMetadata.all) { $0.recordName.eq($2.recordName) }
.select {
Expand Down Expand Up @@ -131,7 +132,7 @@ class RemindersListsModel {
let ids = Array(ids.enumerated())
let (first, rest) = (ids.first!, ids.dropFirst())
$0.position =
rest
rest
.reduce(Case($0.id).when(first.element, then: first.offset)) { cases, id in
cases.when(id.element, then: id.offset)
}
Expand All @@ -143,13 +144,13 @@ class RemindersListsModel {
}

#if DEBUG
func seedDatabaseButtonTapped() {
withErrorReporting {
try database.write { db in
try db.seedSampleData()
func seedDatabaseButtonTapped() {
withErrorReporting {
try database.write { db in
try db.seedSampleData()
}
}
}
}
#endif

@CasePathable
Expand Down Expand Up @@ -191,6 +192,8 @@ class RemindersListsModel {

struct RemindersListsView: View {
@Bindable var model: RemindersListsModel
@State var id = UUID()
@Dependency(\.defaultSyncEngine) var syncEngine

var body: some View {
List {
Expand Down Expand Up @@ -305,14 +308,30 @@ struct RemindersListsView: View {
.listStyle(.insetGrouped)
.toolbar {
#if DEBUG
ToolbarItem(placement: .automatic) {
ToolbarItem(placement: .automatic) {
Menu {
Button {
model.seedDatabaseButtonTapped()
} label: {
Text("Seed data")
Image(systemName: "leaf")
}
Button {
if syncEngine.isRunning {
syncEngine.stop()
id = UUID()
} else {
Task {
await withErrorReporting {
try await syncEngine.start()
}
id = UUID()
}
}
} label: {
Text("\(syncEngine.isRunning ? "Stop" : "Start") synchronizing")
Image(systemName: syncEngine.isRunning ? "stop" : "play")
}
} label: {
Image(systemName: "ellipsis.circle")
}
Expand Down Expand Up @@ -368,6 +387,7 @@ struct RemindersListsView: View {
.navigationDestination(item: $model.destination.detail) { detailModel in
RemindersDetailView(model: detailModel)
}
.id(id)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public struct _SystemFieldsRepresentation<Record: CKRecord>: QueryBindable, Quer
}

public init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
guard let data = try Data?(decoder: &decoder) else {
throw QueryDecodingError.missingRequiredColumn
}
let data = try Data(decoder: &decoder)
let coder = try NSKeyedUnarchiver(forReadingFrom: data)
coder.requiresSecureCoding = true
guard let queryOutput = Record(coder: coder) else {
Expand Down Expand Up @@ -71,9 +69,7 @@ package struct _AllFieldsRepresentation<Record: CKRecord>: QueryBindable, QueryR
}

package init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
guard let data = try Data?(decoder: &decoder) else {
throw QueryDecodingError.missingRequiredColumn
}
let data = try Data(decoder: &decoder)
let coder = try NSKeyedUnarchiver(forReadingFrom: data)
coder.requiresSecureCoding = true
guard let queryOutput = Record(coder: coder) else {
Expand Down Expand Up @@ -166,7 +162,7 @@ extension CKRecord {
let asset = CKAsset(fileURL: URL(hash: newValue))
guard let fileURL = asset.fileURL, (self[key] as? CKAsset)?.fileURL != fileURL
else { return false }
withErrorReporting {
withErrorReporting(.sqliteDataCloudKitFailure) {
try dataManager.save(Data(newValue), to: fileURL)
}
self[key] = asset
Expand Down
2 changes: 1 addition & 1 deletion Sources/SharingGRDBCore/CloudKit/CloudKitSharing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@

public func cloudSharingControllerDidStopSharing(_ csc: UICloudSharingController) {
@Dependency(\.defaultSyncEngine) var syncEngine
withErrorReporting {
withErrorReporting(.sqliteDataCloudKitFailure) {
try syncEngine.deleteShare(recordID: share.recordID)
}
didStopSharing()
Expand Down
8 changes: 8 additions & 0 deletions Sources/SharingGRDBCore/CloudKit/Metadatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ func defaultMetadatabase(
)
.execute(db)
}
migrator.registerMigration("Create PendingRecodZoneChanges Table") { db in
try SQLQueryExpression("""
CREATE TABLE IF NOT EXISTS "\(raw: .sqliteDataCloudKitSchemaName)_pendingRecordZoneChanges" (
"pendingRecordZoneChange" BLOB NOT NULL
) STRICT
""")
.execute(db)
}
try migrator.migrate(metadatabase)
return metadatabase
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#if canImport(CloudKit)
import CloudKit

@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
extension PendingRecordZoneChange {
public nonisolated struct TableColumns: StructuredQueriesCore.TableDefinition {
public typealias QueryValue = PendingRecordZoneChange
public let pendingRecordZoneChange = StructuredQueriesCore.TableColumn<
QueryValue, CKSyncEngine.PendingRecordZoneChange.DataRepresentation
>("pendingRecordZoneChange", keyPath: \QueryValue.pendingRecordZoneChange)
public static var allColumns: [any StructuredQueriesCore.TableColumnExpression] {
[QueryValue.columns.pendingRecordZoneChange]
}
public static var writableColumns: [any StructuredQueriesCore.WritableTableColumnExpression] {
[QueryValue.columns.pendingRecordZoneChange]
}
public var queryFragment: QueryFragment {
"\(self.pendingRecordZoneChange)"
}
}
}

@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
nonisolated extension PendingRecordZoneChange: StructuredQueriesCore.Table {
public nonisolated static var columns: TableColumns {
TableColumns()
}
public nonisolated static var tableName: String {
"sqlitedata_icloud_pendingRecordZoneChanges"
}
public nonisolated init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
let pendingRecordZoneChange = try decoder.decode(
CKSyncEngine.PendingRecordZoneChange.DataRepresentation.self
)
guard let pendingRecordZoneChange else {
throw QueryDecodingError.missingRequiredColumn
}
self.pendingRecordZoneChange = pendingRecordZoneChange
}
}
#endif
64 changes: 64 additions & 0 deletions Sources/SharingGRDBCore/CloudKit/PendingRecordZoneChange.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#if canImport(CloudKit)
import CloudKit

// @Table("\(String.sqliteDataCloudKitSchemaName)_pendingRecordZoneChanges")
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
package struct PendingRecordZoneChange {
// @Column(as: CKSyncEngine.PendingRecordZoneChange.DataRepresentation.self)
package let pendingRecordZoneChange: CKSyncEngine.PendingRecordZoneChange
}

@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
extension PendingRecordZoneChange {
package init(_ pendingRecordZoneChange: CKSyncEngine.PendingRecordZoneChange) {
self.pendingRecordZoneChange = pendingRecordZoneChange
}
}

@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
extension CKSyncEngine.PendingRecordZoneChange {
package struct DataRepresentation: QueryBindable, QueryRepresentable {
package var queryOutput: CKSyncEngine.PendingRecordZoneChange

package init(queryOutput: CKSyncEngine.PendingRecordZoneChange) {
self.queryOutput = queryOutput
}

package var queryBinding: StructuredQueriesCore.QueryBinding {
let archiver = NSKeyedArchiver(requiringSecureCoding: true)
switch queryOutput {
case .saveRecord(let recordID):
recordID.encode(with: archiver)
archiver.encode("saveRecord", forKey: "changeType")
case .deleteRecord(let recordID):
recordID.encode(with: archiver)
archiver.encode("deleteRecord", forKey: "changeType")
@unknown default:
return .invalid(BindingError())
}
return archiver.encodedData.queryBinding
}

package init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws {
let data = try Data(decoder: &decoder)
let coder = try NSKeyedUnarchiver(forReadingFrom: data)
coder.requiresSecureCoding = true
guard let recordID = CKRecord.ID(coder: coder) else {
throw DecodingError()
}
let changeType = coder.decodeObject(of: NSString.self, forKey: "changeType") as? String
switch changeType {
case "saveRecord":
self.init(queryOutput: .saveRecord(recordID))
case "deleteRecord":
self.init(queryOutput: .deleteRecord(recordID))
default:
throw DecodingError()
}
}
}

private struct DecodingError: Error {}
private struct BindingError: Error {}
}
#endif
Loading
Loading