Skip to content

Commit c84b4e9

Browse files
cleanup attachments
1 parent c444032 commit c84b4e9

File tree

6 files changed

+38
-32
lines changed

6 files changed

+38
-32
lines changed

Sources/PowerSync/Protocol/PowerSyncBackendConnector.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public protocol PowerSyncBackendConnectorProtocol: Sendable {
2828
/// 1. Creating credentials for connecting to the PowerSync service.
2929
/// 2. Applying local changes against the backend application server.
3030
///
31-
@MainActor
31+
@MainActor // This class is non-final, we can use actor isolation to make it Sendable
3232
open class PowerSyncBackendConnector: PowerSyncBackendConnectorProtocol {
3333
public init() {}
3434

Sources/PowerSync/attachments/AttachmentContext.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Foundation
22

3-
public protocol AttachmentContext: Sendable {
3+
public protocol AttachmentContextProtocol: Sendable {
44
var db: any PowerSyncDatabaseProtocol { get }
55
var tableName: String { get }
66
var logger: any LoggerProtocol { get }
@@ -44,7 +44,7 @@ public protocol AttachmentContext: Sendable {
4444
func clearQueue() async throws
4545
}
4646

47-
public extension AttachmentContext {
47+
public extension AttachmentContextProtocol {
4848
func deleteAttachment(id: String) async throws {
4949
_ = try await db.execute(
5050
sql: "DELETE FROM \(tableName) WHERE id = ?",
@@ -158,7 +158,7 @@ public extension AttachmentContext {
158158
parameters: [
159159
AttachmentState.archived.rawValue,
160160
limit,
161-
maxArchivedCount,
161+
maxArchivedCount
162162
]
163163
) { cursor in
164164
try Attachment.fromCursor(cursor)
@@ -224,7 +224,7 @@ public extension AttachmentContext {
224224
}
225225

226226
/// Context which performs actions on the attachment records
227-
public actor AttachmentContextImpl: AttachmentContext {
227+
public actor AttachmentContext: AttachmentContextProtocol {
228228
public let db: any PowerSyncDatabaseProtocol
229229
public let tableName: String
230230
public let logger: any LoggerProtocol

Sources/PowerSync/attachments/AttachmentQueue.swift

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ public let defaultAttachmentsTableName = "attachments"
66

77
public protocol AttachmentQueueProtocol: Sendable {
88
var db: any PowerSyncDatabaseProtocol { get }
9-
var attachmentsService: any AttachmentService { get }
9+
var attachmentsService: any AttachmentServiceProtocol { get }
1010
var localStorage: any LocalStorageAdapter { get }
11-
var syncingService: any SyncingService { get }
1211
var downloadAttachments: Bool { get }
1312

1413
/// Starts the attachment sync process
@@ -226,15 +225,6 @@ public extension AttachmentQueueProtocol {
226225
}
227226
}
228227
}
229-
230-
func expireCache() async throws {
231-
try await attachmentsService.withContext { context in
232-
var done = false
233-
repeat {
234-
done = try await self.syncingService.deleteArchivedAttachments(context)
235-
} while !done
236-
}
237-
}
238228
}
239229

240230
/// Class used to implement the attachment queue
@@ -284,7 +274,7 @@ public actor AttachmentQueue: AttachmentQueueProtocol {
284274
public let logger: any LoggerProtocol
285275

286276
/// Attachment service for interacting with attachment records
287-
public let attachmentsService: AttachmentService
277+
public let attachmentsService: AttachmentServiceProtocol
288278

289279
private var syncStatusTask: Task<Void, Error>?
290280
private var cancellables = Set<AnyCancellable>()
@@ -337,7 +327,7 @@ public actor AttachmentQueue: AttachmentQueueProtocol {
337327
logger: self.logger,
338328
maxArchivedCount: archivedCacheLimit
339329
)
340-
syncingService = SyncingServiceImpl(
330+
syncingService = SyncingService(
341331
remoteStorage: self.remoteStorage,
342332
localStorage: self.localStorage,
343333
attachmentsService: attachmentsService,
@@ -448,6 +438,15 @@ public actor AttachmentQueue: AttachmentQueueProtocol {
448438
}
449439
}
450440

441+
public func expireCache() async throws {
442+
try await attachmentsService.withContext { context in
443+
var done = false
444+
repeat {
445+
done = try await self.syncingService.deleteArchivedAttachments(context)
446+
} while !done
447+
}
448+
}
449+
451450
/// Verifies attachment records are present in the filesystem
452451
private func verifyAttachments(context: AttachmentContext) async throws {
453452
let attachments = try await context.getAttachments()

Sources/PowerSync/attachments/AttachmentService.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Foundation
22

3-
public protocol AttachmentService: Sendable {
3+
public protocol AttachmentServiceProtocol: Sendable {
44
/// Watches for changes to the attachments table.
55
func watchActiveAttachments() async throws -> AsyncThrowingStream<[String], Error>
66

@@ -11,7 +11,7 @@ public protocol AttachmentService: Sendable {
1111
}
1212

1313
/// Service which manages attachment records.
14-
actor AttachmentServiceImpl: AttachmentService {
14+
actor AttachmentServiceImpl: AttachmentServiceProtocol {
1515
private let db: any PowerSyncDatabaseProtocol
1616
private let tableName: String
1717
private let logger: any LoggerProtocol
@@ -29,7 +29,7 @@ actor AttachmentServiceImpl: AttachmentService {
2929
self.db = db
3030
self.tableName = tableName
3131
self.logger = logger
32-
context = AttachmentContextImpl(
32+
context = AttachmentContext(
3333
db: db,
3434
tableName: tableName,
3535
logger: logger,
@@ -63,7 +63,10 @@ actor AttachmentServiceImpl: AttachmentService {
6363
}
6464
}
6565

66-
public func withContext<R: Sendable>(callback: @Sendable @escaping (AttachmentContext) async throws -> R) async throws -> R {
66+
public func withContext<R: Sendable>(
67+
callback: @Sendable @escaping (AttachmentContext
68+
) async throws -> R) async throws -> R
69+
{
6770
try await callback(context)
6871
}
6972
}

Sources/PowerSync/attachments/FileManagerLocalStorage.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import Foundation
44
* Implementation of LocalStorageAdapter using FileManager
55
*/
66
public actor FileManagerStorageAdapter: LocalStorageAdapter {
7-
private let fileManager = FileManager.default
7+
private let fileManager: FileManager
88

9-
public init() {}
9+
public init(
10+
fileManager: FileManager? = nil
11+
) {
12+
self.fileManager = fileManager ?? FileManager.default
13+
}
1014

1115
public func saveFile(filePath: String, data: Data) async throws -> Int64 {
1216
return try await Task {

Sources/PowerSync/attachments/SyncingService.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Foundation
66
/// This watches for changes to active attachments and performs queued
77
/// download, upload, and delete operations. Syncs can be triggered manually,
88
/// periodically, or based on database changes.
9-
public protocol SyncingService: Sendable {
9+
public protocol SyncingServiceProtocol: Sendable {
1010
/// Starts periodic syncing of attachments.
1111
///
1212
/// - Parameter period: The time interval in seconds between each sync.
@@ -26,10 +26,10 @@ public protocol SyncingService: Sendable {
2626
func deleteArchivedAttachments(_ context: AttachmentContext) async throws -> Bool
2727
}
2828

29-
actor SyncingServiceImpl: SyncingService {
29+
public actor SyncingService: SyncingServiceProtocol {
3030
private let remoteStorage: RemoteStorageAdapter
3131
private let localStorage: LocalStorageAdapter
32-
private let attachmentsService: AttachmentService
32+
private let attachmentsService: AttachmentServiceProtocol
3333
private let getLocalUri: @Sendable (String) async -> String
3434
private let errorHandler: SyncErrorHandler?
3535
private let syncThrottle: TimeInterval
@@ -54,7 +54,7 @@ actor SyncingServiceImpl: SyncingService {
5454
public init(
5555
remoteStorage: RemoteStorageAdapter,
5656
localStorage: LocalStorageAdapter,
57-
attachmentsService: AttachmentService,
57+
attachmentsService: AttachmentServiceProtocol,
5858
logger: any LoggerProtocol,
5959
getLocalUri: @Sendable @escaping (String) async -> String,
6060
errorHandler: SyncErrorHandler? = nil,
@@ -106,23 +106,23 @@ actor SyncingServiceImpl: SyncingService {
106106
}
107107

108108
/// Cleans up internal resources and cancels any ongoing syncing.
109-
func close() async throws {
109+
public func close() async throws {
110110
try guardClosed()
111111

112112
try await _stopSync()
113113
closed = true
114114
}
115115

116116
/// Triggers a sync operation. Can be called manually.
117-
func triggerSync() async throws {
117+
public func triggerSync() async throws {
118118
try guardClosed()
119119
syncTriggerSubject.send(())
120120
}
121121

122122
/// Deletes attachments marked as archived that exist on local storage.
123123
///
124124
/// - Returns: `true` if any deletions occurred, `false` otherwise.
125-
func deleteArchivedAttachments(_ context: AttachmentContext) async throws -> Bool {
125+
public func deleteArchivedAttachments(_ context: AttachmentContext) async throws -> Bool {
126126
return try await context.deleteArchivedAttachments { pendingDelete in
127127
for attachment in pendingDelete {
128128
guard let localUri = attachment.localUri else { continue }
@@ -149,7 +149,7 @@ actor SyncingServiceImpl: SyncingService {
149149
.sink { _ in continuation.yield(()) }
150150

151151
continuation.onTermination = { _ in
152-
continuation.finish()
152+
cancellable.cancel()
153153
}
154154
self.cancellables.insert(cancellable)
155155
}

0 commit comments

Comments
 (0)