Skip to content

Commit b17b4d4

Browse files
authored
Add sync progress APIs (#45)
1 parent 916211d commit b17b4d4

File tree

7 files changed

+146
-11
lines changed

7 files changed

+146
-11
lines changed

.github/workflows/docs.yaml

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ name: Deploy Docs
22

33
on:
44
push:
5-
branches:
6-
- main
75

86
permissions:
97
contents: read
@@ -47,6 +45,7 @@ jobs:
4745

4846
# Deployment job
4947
deploy:
48+
if: ${{ github.ref == 'refs/heads/main' }}
5049
environment:
5150
name: github-pages
5251
url: ${{ needs.build.outputs.page_url }}

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## unreleased
4+
5+
- Add sync progress information through `SyncStatusData.downloadProgress`.
6+
37
# 1.0.0
48

59
- Improved the stability of watched queries. Watched queries were previously susceptible to runtime crashes if an exception was thrown in the update stream. Errors are now gracefully handled.

Demo/PowerSyncExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"originHash" : "33297127250b66812faa920958a24bae46bf9e9d1c38ea6b84ca413efaf16afd",
2+
"originHash" : "2d885a1b46f17f9239b7876e3889168a6de98024718f2d7af03aede290c8a86a",
33
"pins" : [
44
{
55
"identity" : "anycodable",
@@ -10,6 +10,15 @@
1010
"version" : "0.6.7"
1111
}
1212
},
13+
{
14+
"identity" : "powersync-kotlin",
15+
"kind" : "remoteSourceControl",
16+
"location" : "https://github.com/powersync-ja/powersync-kotlin.git",
17+
"state" : {
18+
"revision" : "ccd2e595195c59d570eb93a878ad6a5cfca72ada",
19+
"version" : "1.0.1+SWIFT.0"
20+
}
21+
},
1322
{
1423
"identity" : "powersync-sqlite-core-swift",
1524
"kind" : "remoteSourceControl",

Demo/PowerSyncExample/Components/ListView.swift

+30-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import SwiftUI
22
import IdentifiedCollections
33
import SwiftUINavigation
4+
import PowerSync
45

56
struct ListView: View {
67
@Environment(SystemManager.self) private var system
@@ -9,18 +10,32 @@ struct ListView: View {
910
@State private var error: Error?
1011
@State private var newList: NewListContent?
1112
@State private var editing: Bool = false
12-
@State private var didSync: Bool = false
13+
@State private var status: SyncStatusData? = nil
1314

1415
var body: some View {
15-
if !didSync {
16-
Text("Busy with sync!").task {
17-
do {
18-
try await system.db.waitForFirstSync(priority: 1)
19-
didSync = true;
20-
} catch {}
16+
if status?.hasSynced != true {
17+
VStack {
18+
if let status = self.status {
19+
if status.hasSynced != true {
20+
Text("Busy with initial sync...")
21+
22+
if let progress = status.downloadProgress {
23+
ProgressView(value: progress.fraction)
24+
25+
if progress.downloadedOperations == progress.totalOperations {
26+
Text("Applying server-side changes...")
27+
} else {
28+
Text("Downloaded \(progress.downloadedOperations) out of \(progress.totalOperations)")
29+
}
30+
}
31+
}
32+
} else {
33+
ProgressView()
34+
.progressViewStyle(CircularProgressViewStyle())
35+
}
2136
}
2237
}
23-
38+
2439
List {
2540
if let error {
2641
ErrorText(error)
@@ -79,6 +94,13 @@ struct ListView: View {
7994
}
8095
}
8196
}
97+
.task {
98+
self.status = system.db.currentStatus
99+
100+
for await status in system.db.currentStatus.asFlow() {
101+
self.status = status
102+
}
103+
}
82104
}
83105

84106
func handleDelete(at offset: IndexSet) async {

Sources/PowerSync/Kotlin/sync/KotlinSyncStatusData.swift

+35
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ extension KotlinSyncStatusDataProtocol {
3737
)
3838
}
3939

40+
var downloadProgress: (any SyncDownloadProgress)? {
41+
guard let kotlinProgress = base.downloadProgress else { return nil }
42+
return KotlinSyncDownloadProgress(progress: kotlinProgress)
43+
}
44+
4045
var hasSynced: Bool? {
4146
base.hasSynced?.boolValue
4247
}
@@ -80,3 +85,33 @@ extension KotlinSyncStatusDataProtocol {
8085
)
8186
}
8287
}
88+
89+
protocol KotlinProgressWithOperationsProtocol: ProgressWithOperations {
90+
var base: any PowerSyncKotlin.ProgressWithOperations { get }
91+
}
92+
93+
extension KotlinProgressWithOperationsProtocol {
94+
var totalOperations: Int32 {
95+
return base.totalOperations
96+
}
97+
98+
var downloadedOperations: Int32 {
99+
return base.downloadedOperations
100+
}
101+
}
102+
103+
struct KotlinProgressWithOperations: KotlinProgressWithOperationsProtocol {
104+
let base: PowerSyncKotlin.ProgressWithOperations
105+
}
106+
107+
struct KotlinSyncDownloadProgress: KotlinProgressWithOperationsProtocol, SyncDownloadProgress {
108+
let progress: PowerSyncKotlin.SyncDownloadProgress
109+
110+
var base: any PowerSyncKotlin.ProgressWithOperations {
111+
progress
112+
}
113+
114+
func untilPriority(priority: BucketPriority) -> any ProgressWithOperations {
115+
return KotlinProgressWithOperations(base: progress.untilPriority(priority: priority.priorityCode))
116+
}
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/// Information about a progressing download.
2+
///
3+
/// This reports the ``totalOperations`` amount of operations to download, how many of them
4+
/// have already been downloaded as ``downloadedOperations`` and finally a ``fraction`` indicating
5+
/// relative progress.
6+
///
7+
/// To obtain a ``ProgressWithOperations`` instance, either use ``SyncStatusData/downloadProgress``
8+
/// for global progress or ``SyncDownloadProgress/untilPriority(priority:)``.
9+
public protocol ProgressWithOperations {
10+
/// How many operations need to be downloaded in total for the current download
11+
/// to complete.
12+
var totalOperations: Int32 { get }
13+
14+
/// How many operations, out of ``totalOperations``, have already been downloaded.
15+
var downloadedOperations: Int32 { get }
16+
}
17+
18+
public extension ProgressWithOperations {
19+
/// The relative amount of ``totalOperations`` to items in ``downloadedOperations``, as a
20+
/// number between `0.0` and `1.0` (inclusive).
21+
///
22+
/// When this number reaches `1.0`, all changes have been received from the sync service.
23+
/// Actually applying these changes happens before the ``SyncStatusData/downloadProgress``
24+
/// field is cleared though, so progress can stay at `1.0` for a short while before completing.
25+
var fraction: Float {
26+
if (self.totalOperations == 0) {
27+
return 0.0
28+
}
29+
30+
return Float.init(self.downloadedOperations) / Float.init(self.totalOperations)
31+
}
32+
}
33+
34+
/// Provides realtime progress on how PowerSync is downloading rows.
35+
///
36+
/// This type reports progress by extending ``ProgressWithOperations``, meaning that the
37+
/// ``ProgressWithOperations/totalOperations``, ``ProgressWithOperations/downloadedOperations``
38+
/// and ``ProgressWithOperations/fraction`` properties are available on this instance.
39+
/// Additionally, it's possible to obtain progress towards a specific priority only (instead
40+
/// of tracking progress for the entire download) by using ``untilPriority(priority:)``.
41+
///
42+
/// The reported progress always reflects the status towards the end of a sync iteration (after
43+
/// which a consistent snapshot of all buckets is available locally).
44+
///
45+
/// In rare cases (in particular, when a [compacting](https://docs.powersync.com/usage/lifecycle-maintenance/compacting-buckets)
46+
/// operation takes place between syncs), it's possible for the returned numbers to be slightly
47+
/// inaccurate. For this reason, ``SyncDownloadProgress`` should be seen as an approximation of progress.
48+
/// The information returned is good enough to build progress bars, but not exaxt enough to track
49+
/// individual download counts.
50+
///
51+
/// Also note that data is downloaded in bulk, which means that individual counters are unlikely
52+
/// to be updated one-by-one.
53+
public protocol SyncDownloadProgress: ProgressWithOperations {
54+
/// Returns download progress towardss all data up until the specified `priority`
55+
/// being received.
56+
///
57+
/// The returned ``ProgressWithOperations`` instance tracks the target amount of operations that
58+
/// need to be downloaded in total and how many of them have already been received.
59+
func untilPriority(priority: BucketPriority) -> ProgressWithOperations
60+
}

Sources/PowerSync/Protocol/sync/SyncStatusData.swift

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ public protocol SyncStatusData {
1111
/// Indicates whether the system is actively downloading changes.
1212
var downloading: Bool { get }
1313

14+
/// Realtime progress information about downloaded operations during an active sync.
15+
///
16+
/// For more information on what progress is reported, see ``SyncDownloadProgress``.
17+
/// This value will be non-null only if ``downloading`` is `true`.
18+
var downloadProgress: SyncDownloadProgress? { get }
19+
1420
/// Indicates whether the system is actively uploading changes.
1521
var uploading: Bool { get }
1622

0 commit comments

Comments
 (0)