|
5 | 5 | import OrderedCollections |
6 | 6 | import OSLog |
7 | 7 | import Observation |
| 8 | + import Perception |
8 | 9 | import StructuredQueriesCore |
9 | 10 | import SwiftData |
10 | 11 | import TabularData |
|
423 | 424 | } |
424 | 425 | } |
425 | 426 |
|
| 427 | + private let _isReady = LockIsolated(false) |
| 428 | + private var isReady: Bool { |
| 429 | + get { |
| 430 | + observationRegistrar.access(self, keyPath: \.isReady) |
| 431 | + return _isReady.withValue(\.self) |
| 432 | + } |
| 433 | + set { |
| 434 | + observationRegistrar.withMutation(of: self, keyPath: \.isReady) { |
| 435 | + _isReady.withValue { $0 = newValue } |
| 436 | + } |
| 437 | + } |
| 438 | + } |
| 439 | + |
426 | 440 | /// Determines if the sync engine is currently running or not. |
427 | 441 | public var isRunning: Bool { |
428 | 442 | observationRegistrar.access(self, keyPath: \.isRunning) |
|
440 | 454 | private: privateSyncEngine, |
441 | 455 | shared: sharedSyncEngine |
442 | 456 | ) |
| 457 | + privateSyncEngine.state.add(pendingDatabaseChanges: [.saveZone(defaultZone)]) |
443 | 458 | } |
444 | 459 | } |
445 | | - syncEngines.withValue { |
446 | | - $0.private?.state.add(pendingDatabaseChanges: [.saveZone(defaultZone)]) |
447 | | - } |
448 | 460 |
|
449 | 461 | let previousRecordTypes = try metadatabase.read { db in |
450 | 462 | try RecordType.all.fetchAll(db) |
|
492 | 504 | ($0.tableName, $0) |
493 | 505 | } |
494 | 506 | ) |
| 507 | + |
| 508 | + withErrorReporting(.sqliteDataCloudKitFailure) { |
| 509 | + try uploadRecordsToCloudKit( |
| 510 | + previousRecordTypeByTableName: previousRecordTypeByTableName, |
| 511 | + currentRecordTypeByTableName: currentRecordTypeByTableName |
| 512 | + ) |
| 513 | + } |
| 514 | + |
495 | 515 | return Task { |
496 | 516 | await withErrorReporting(.sqliteDataCloudKitFailure) { |
497 | 517 | guard try await container.accountStatus() == .available |
498 | 518 | else { return } |
499 | | - try await uploadRecordsToCloudKit( |
500 | | - previousRecordTypeByTableName: previousRecordTypeByTableName, |
501 | | - currentRecordTypeByTableName: currentRecordTypeByTableName |
502 | | - ) |
| 519 | + |
503 | 520 | try await updateLocalFromSchemaChange( |
504 | 521 | previousRecordTypeByTableName: previousRecordTypeByTableName, |
505 | 522 | currentRecordTypeByTableName: currentRecordTypeByTableName |
506 | 523 | ) |
507 | 524 | try await cacheUserTables(recordTypes: currentRecordTypes) |
508 | 525 | } |
| 526 | + isReady = true |
509 | 527 | } |
510 | 528 | } |
511 | | - |
| 529 | + |
512 | 530 | /// Fetches pending remote changes from the server. |
513 | 531 | /// |
514 | 532 | /// Use this method to ensure the sync engine immediately fetches all pending remote changes |
|
520 | 538 | public func fetchChanges( |
521 | 539 | _ options: CKSyncEngine.FetchChangesOptions = CKSyncEngine.FetchChangesOptions() |
522 | 540 | ) async throws { |
| 541 | + await isReady() |
523 | 542 | let (privateSyncEngine, sharedSyncEngine) = syncEngines.withValue { |
524 | 543 | ($0.private, $0.shared) |
525 | 544 | } |
|
529 | 548 | async let shared: Void = sharedSyncEngine.fetchChanges(options) |
530 | 549 | _ = try await (`private`, shared) |
531 | 550 | } |
532 | | - |
| 551 | + |
533 | 552 | /// Sends pending local changes to the server. |
534 | 553 | /// |
535 | 554 | /// Use this method to ensure the sync engine sends all pending local changes to the server |
|
541 | 560 | public func sendChanges( |
542 | 561 | _ options: CKSyncEngine.SendChangesOptions = CKSyncEngine.SendChangesOptions() |
543 | 562 | ) async throws { |
| 563 | + await isReady() |
544 | 564 | let (privateSyncEngine, sharedSyncEngine) = syncEngines.withValue { |
545 | 565 | ($0.private, $0.shared) |
546 | 566 | } |
|
550 | 570 | async let shared: Void = sharedSyncEngine.sendChanges(options) |
551 | 571 | _ = try await (`private`, shared) |
552 | 572 | } |
553 | | - |
| 573 | + |
| 574 | + private func isReady() async { |
| 575 | + guard !isRunning else { return } |
| 576 | + _ = await Perceptions { self.isReady }.first(where: { $0 }) |
| 577 | + } |
| 578 | + |
554 | 579 | /// Synchronizes local and remote pending changes. |
555 | 580 | /// |
556 | 581 | /// Use this method to ensure the sync engine immediately fetches all pending remote changes |
|
580 | 605 | private func uploadRecordsToCloudKit( |
581 | 606 | previousRecordTypeByTableName: [String: RecordType], |
582 | 607 | currentRecordTypeByTableName: [String: RecordType] |
583 | | - ) async throws { |
584 | | - try await enqueueLocallyPendingChanges() |
585 | | - try await userDatabase.write { db in |
| 608 | + ) throws { |
| 609 | + try enqueueLocallyPendingChanges() |
| 610 | + try userDatabase.write { db in |
586 | 611 | try PendingRecordZoneChange.delete().execute(db) |
587 | 612 |
|
588 | 613 | let newTableNames = currentRecordTypeByTableName.keys.filter { tableName in |
|
597 | 622 | } |
598 | 623 | } |
599 | 624 |
|
600 | | - private func enqueueLocallyPendingChanges() async throws { |
601 | | - let pendingRecordZoneChanges = try await metadatabase.read { db in |
| 625 | + private func enqueueLocallyPendingChanges() throws { |
| 626 | + let pendingRecordZoneChanges = try metadatabase.read { db in |
602 | 627 | try PendingRecordZoneChange |
603 | 628 | .select(\.pendingRecordZoneChange) |
604 | 629 | .fetchAll(db) |
|
1858 | 1883 | } |
1859 | 1884 |
|
1860 | 1885 | private func refreshLastKnownServerRecord(_ record: CKRecord) async { |
1861 | | - await withErrorReporting(.sqliteDataCloudKitFailure) { |
| 1886 | + //await withErrorReporting(.sqliteDataCloudKitFailure) { |
| 1887 | + do { |
1862 | 1888 | try await metadatabase.write { db in |
1863 | 1889 | let metadata = try SyncMetadata.find(record.recordID).fetchOne(db) |
1864 | 1890 | func updateLastKnownServerRecord() throws { |
|
1876 | 1902 | try updateLastKnownServerRecord() |
1877 | 1903 | } |
1878 | 1904 | } |
| 1905 | + } catch { |
| 1906 | + print(error) |
| 1907 | + print("!!!") |
1879 | 1908 | } |
1880 | 1909 | } |
1881 | 1910 |
|
|
0 commit comments