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
16 changes: 8 additions & 8 deletions Examples/Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
CA5E47072DECEF0F0069E0F8 /* InlineSnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = CA5E47062DECEF0F0069E0F8 /* InlineSnapshotTesting */; };
CA5E47092DECEFC80069E0F8 /* SnapshotTestingCustomDump in Frameworks */ = {isa = PBXBuildFile; productRef = CA5E47082DECEFC80069E0F8 /* SnapshotTestingCustomDump */; };
CA5E470B2DECF0280069E0F8 /* DependenciesTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = CA5E470A2DECF0280069E0F8 /* DependenciesTestSupport */; };
CA6A1D242E68A0A600604D6A /* SQLiteData in Frameworks */ = {isa = PBXBuildFile; productRef = CA6A1D232E68A0A600604D6A /* SQLiteData */; };
CAD001872D874F1F00FA977A /* DependenciesTestSupport in Frameworks */ = {isa = PBXBuildFile; productRef = CAD001862D874F1F00FA977A /* DependenciesTestSupport */; };
DC5FA7482D4C63D60082743E /* DependenciesMacros in Frameworks */ = {isa = PBXBuildFile; productRef = DC5FA7472D4C63D60082743E /* DependenciesMacros */; };
DCBE8A142D4842BF0071F499 /* CasePaths in Frameworks */ = {isa = PBXBuildFile; productRef = DCBE8A132D4842BF0071F499 /* CasePaths */; };
DCD9AC8B2E02176700FB20F8 /* SharingGRDB in Frameworks */ = {isa = PBXBuildFile; productRef = DCD9AC8A2E02176700FB20F8 /* SharingGRDB */; };
DCD9AC8D2E02177200FB20F8 /* SharingGRDB in Frameworks */ = {isa = PBXBuildFile; productRef = DCD9AC8C2E02177200FB20F8 /* SharingGRDB */; };
DCD9AC8F2E02177900FB20F8 /* SharingGRDB in Frameworks */ = {isa = PBXBuildFile; productRef = DCD9AC8E2E02177900FB20F8 /* SharingGRDB */; };
DCF267392D48437300B680BE /* SwiftUINavigation in Frameworks */ = {isa = PBXBuildFile; productRef = DCF267382D48437300B680BE /* SwiftUINavigation */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -163,8 +163,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CA6A1D242E68A0A600604D6A /* SQLiteData in Frameworks */,
CA14DBC92DA884C400E36852 /* CasePaths in Frameworks */,
DCD9AC8D2E02177200FB20F8 /* SharingGRDB in Frameworks */,
CA5E46912DEBB8570069E0F8 /* SwiftUINavigation in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -337,7 +337,7 @@
packageProductDependencies = (
CA14DBC82DA884C400E36852 /* CasePaths */,
CA5E46902DEBB8570069E0F8 /* SwiftUINavigation */,
DCD9AC8C2E02177200FB20F8 /* SharingGRDB */,
CA6A1D232E68A0A600604D6A /* SQLiteData */,
);
productName = Reminders;
productReference = CAF836D82D4735AB0047AEB5 /* Reminders.app */;
Expand Down Expand Up @@ -1076,6 +1076,11 @@
package = DC5FA7462D4C63D60082743E /* XCRemoteSwiftPackageReference "swift-dependencies" */;
productName = DependenciesTestSupport;
};
CA6A1D232E68A0A600604D6A /* SQLiteData */ = {
isa = XCSwiftPackageProductDependency;
package = DCD9AC892E02176700FB20F8 /* XCLocalSwiftPackageReference "../../sharing-grdb" */;
productName = SQLiteData;
};
CAD001862D874F1F00FA977A /* DependenciesTestSupport */ = {
isa = XCSwiftPackageProductDependency;
package = DC5FA7462D4C63D60082743E /* XCRemoteSwiftPackageReference "swift-dependencies" */;
Expand All @@ -1095,11 +1100,6 @@
isa = XCSwiftPackageProductDependency;
productName = SharingGRDB;
};
DCD9AC8C2E02177200FB20F8 /* SharingGRDB */ = {
isa = XCSwiftPackageProductDependency;
package = DCD9AC892E02176700FB20F8 /* XCLocalSwiftPackageReference "../../sharing-grdb" */;
productName = SharingGRDB;
};
DCD9AC8E2E02177900FB20F8 /* SharingGRDB */ = {
isa = XCSwiftPackageProductDependency;
package = DCD9AC892E02176700FB20F8 /* XCLocalSwiftPackageReference "../../sharing-grdb" */;
Expand Down
1 change: 1 addition & 0 deletions Examples/Reminders/RemindersDetail.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import CasePaths
import CloudKit
import Sharing
import SQLiteData
import SwiftUI
import SwiftUINavigation
Expand Down
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ let package = Package(
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.4"),
.package(
url: "https://github.com/pointfreeco/swift-structured-queries",
from: "0.16.0",
from: "0.17.0",
traits: [
.trait(name: "StructuredQueriesTagged", condition: .when(traits: ["SQLiteDataTagged"]))
]
Expand Down
2 changes: 1 addition & 1 deletion Package@swift-6.0.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ let package = Package(
.package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.9.0"),
.package(url: "https://github.com/pointfreeco/swift-sharing", from: "2.3.0"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.4"),
.package(url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.16.0"),
.package(url: "https://github.com/pointfreeco/swift-structured-queries", from: "0.17.0"),
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "1.5.0"),
],
targets: [
Expand Down
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ var items: [Item]

@Table
struct Item {
let id: Int
let id: UUID
var title = ""
var isInStock = true
var notes = ""
Expand Down Expand Up @@ -178,6 +178,9 @@ var items



@FetchAll(Item.order(by: \.isInStock))
var items

@FetchOne(Item.count())
var itemsCount = 0

Expand All @@ -198,6 +201,9 @@ var items: [Item]
})
var items: [Item]

// No @Query equivalent of ordering
// by boolean column.

// No @Query equivalent of counting
// entries in database without loading
// all entries.
Expand Down Expand Up @@ -303,14 +309,16 @@ See the following benchmarks against
taste of how it compares:

```
Orders.fetchAll setup rampup duration
SQLite (generated by Enlighter 1.4.10) 0 0.144 7.183
Lighter (1.4.10) 0 0.164 8.059
SQLiteData (0.2.0) 0 0.172 8.511
GRDB (7.4.1, manual decoding) 0 0.376 18.819
SQLite.swift (0.15.3, manual decoding) 0 0.564 27.994
SQLite.swift (0.15.3, Codable) 0 0.863 43.261
GRDB (7.4.1, Codable) 0.002 1.07 53.326
Orders.fetchAll setup rampup duration
SQLite (generated by Enlighter 1.4.10) 0 0.144 7.183
Lighter (1.4.10) 0 0.164 8.059
┌──────────────────────────────────────────────────────────────────┐
│ SQLiteData (1.0.0) 0 0.172 8.511 │
└──────────────────────────────────────────────────────────────────┘
GRDB (7.4.1, manual decoding) 0 0.376 18.819
SQLite.swift (0.15.3, manual decoding) 0 0.564 27.994
SQLite.swift (0.15.3, Codable) 0 0.863 43.261
GRDB (7.4.1, Codable) 0.002 1.07 53.326
```

## SQLite knowledge required
Expand Down
48 changes: 38 additions & 10 deletions Sources/SQLiteData/CloudKit/CloudKitSharing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import UIKit
#endif

/// A shared record that can be used to present a ``CloudSharingView``.
///
/// See <doc:CloudKitSharing#Creating-CKShare-records> for more information.,
@available(iOS 15, tvOS 15, macOS 12, watchOS 8, *)
public struct SharedRecord: Hashable, Identifiable, Sendable {
let container: any CloudContainer
Expand Down Expand Up @@ -43,7 +46,26 @@
"The record could not be shared."
}
}


/// Shares a record in CloudKit.
///
/// This method will thrown an error if:
///
/// * The table the `record` belongs to is not synchronized to CloudKit.
/// * The `record` has any foreign keys. Only root records are shareable in CloudKit.
/// * The table the `record` belongs to is a "private" table as determined by the
/// [`SyncEngine` initializer](<doc:SyncEngine/init(for:tables:privateTables:containerIdentifier:defaultZone:startImmediately:logger:)>).
/// * The `record` is being shared before it has been synchronized to CloudKit.
/// * Any of the CloudKit APIs invoked throw an error.
///
/// The value returned from this method can be used to present a ``CloudSharingView`` which
/// allows the user to send a share URL to another user.
///
/// - Parameters:
/// - record: The record to be shared on CloudKit.
/// - configure: A trailing closure that can customize the `CKShare` sent to CloudKit. See
/// [Apple's documentation](https://developer.apple.com/documentation/cloudkit/ckshare/systemfieldkey)
/// for more info on what can be configured.
public func share<T: PrimaryKeyedTable>(
record: T,
configure: @Sendable (CKShare) -> Void
Expand All @@ -56,7 +78,8 @@
recordPrimaryKey: record.primaryKey.rawIdentifier,
reason: .recordTableNotSynchronized,
debugDescription: """
Table is not shareable: table type not passed to 'tables' parameter of 'SyncEngine.init'.
Table is not shareable: table type not passed to 'tables' parameter of \
'SyncEngine.init'.
"""
)
}
Expand Down Expand Up @@ -116,7 +139,6 @@
return try await container.database(for: rootRecord.recordID)
.record(for: shareRecordID) as? CKShare
} catch let error as CKError where error.code == .unknownItem {
reportIssue("This would have been a problem before")
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't remember what this report issue is about, but doesnt seem very helpful.

return nil
}
}
Expand All @@ -133,8 +155,6 @@
)

configure(sharedRecord)
// TODO: We are getting an "client oplock error updating record" error in the logs when
// creating new shares / editing existing shares.
_ = try await container.privateCloudDatabase.modifyRecords(
saving: [sharedRecord, rootRecord],
deleting: []
Expand Down Expand Up @@ -173,13 +193,21 @@
)
try result?.deleteResults.values.forEach { _ = try $0.get() }
}


/// Accepts a shared record.
///
/// This method should be invoked from various delegate methods on the scene delegate of the
/// app. See <doc:CloudKitSharing#Accepting-shared-records> for more info.
public func acceptShare(metadata: CKShare.Metadata) async throws {
try await acceptShare(metadata: ShareMetadata(rawValue: metadata))
}
}

#if canImport(UIKit) && !os(watchOS)
#if canImport(UIKit) && !os(watchOS)
/// A view that presents standard screens for adding and removing people from a CloudKit share \
/// record.
///
/// See <doc:CloudKitSharing#Creating-CKShare-records> for more info.
@available(iOS 17, macOS 14, tvOS 17, *)
public struct CloudSharingView: UIViewControllerRepresentable {
let sharedRecord: SharedRecord
Expand All @@ -204,8 +232,8 @@
self.syncEngine = syncEngine
}

public func makeCoordinator() -> CloudSharingDelegate {
CloudSharingDelegate(
public func makeCoordinator() -> _CloudSharingDelegate {
_CloudSharingDelegate(
share: sharedRecord.share,
didFinish: didFinish,
didStopSharing: didStopSharing,
Expand All @@ -231,7 +259,7 @@
}

@available(iOS 17, macOS 14, tvOS 17, *)
public final class CloudSharingDelegate: NSObject, UICloudSharingControllerDelegate {
public final class _CloudSharingDelegate: NSObject, UICloudSharingControllerDelegate {
let share: CKShare
let didFinish: (Result<Void, Error>) -> Void
let didStopSharing: () -> Void
Expand Down
37 changes: 37 additions & 0 deletions Sources/SQLiteData/CloudKit/DefaultSyncEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,43 @@

@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
extension DependencyValues {
/// The default sync engine used by the application.
///
/// Configure this as early as possible in your app's lifetime, like the app entry point in
/// SwiftUI, using `prepareDependencies`:
///
/// ```swift
/// import SQLiteData
/// import SwiftUI
///
/// ```swift
/// @main
/// struct MyApp: App {
/// init() {
/// prepareDependencies {
/// $0.defaultDatabase = try! appDatabase()
/// $0.defaultSyncEngine = SyncEngine(
/// for: $0.defaultDatabase,
/// tables: Item.self
/// )
/// }
/// }
/// // ...
/// }
/// ```
///
/// > Note: You can only prepare the default sync engine a single time in the lifetime of
/// > your app. Attempting to do so more than once will produce a runtime warning.
///
/// Once configured, access the default sync engine anywhere using `@Dependency`:
///
/// ```swift
/// @Dependency(\.defaultSyncEngine) var syncEngine
///
/// syncEngine.acceptShare(metadata: metadata)
/// ```
///
/// See <doc:PreparingDatabase> for more info.
public var defaultSyncEngine: SyncEngine {
get { self[SyncEngine.self] }
set { self[SyncEngine.self] = newValue }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation

/// A type that can be represented by a string identifier.
public protocol IdentifierStringConvertible {
init?(rawIdentifier: String)
var rawIdentifier: String { get }
Expand Down
8 changes: 0 additions & 8 deletions Sources/SQLiteData/CloudKit/Internal/MockCloudDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,6 @@ package final class MockCloudDatabase: CloudDatabase {
// We are trying to save a record that does not have a change tag yet also already
// exists in the DB. This means the user has created a new CKRecord from scratch,
// giving it a new identity, rather than leveraging an existing CKRecord.
reportIssue(
"""
A new identity was created for an existing 'CKRecord' \
('\(existingRecord.recordID.recordName)'). Rather than creating \
'CKRecord' from scratch for an existing record, use the database to fetch the \
current record.
"""
)
saveResults[recordToSave.recordID] = .failure(
CKError(
.serverRejectedRequest,
Expand Down
Loading
Loading