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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Fix `SyncStatus.asFlow()` emitting the same object, causing SwiftUI to miss updates.
* Add the `ObservableSyncStatus` utility, which can be used to track Sync Status updates through an `@Observable` class.
* Clear `SyncStatus.uploadError` after a successful upload.
* Allow passing a custom `URLSession` in `SyncClientConfiguration`.

## 1.14.4

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ final class SystemManager {

func connect() async {
do {
// Passing a custom URL session is not required, but it can be used to intercept HTTP requests
// or to configure additional headers like shown here.
let config = URLSessionConfiguration.ephemeral
config.httpAdditionalHeaders = ["x-my-custom-header": "example"]
let session = URLSession(configuration: config)

try await db.connect(
connector: connector,
options: ConnectOptions(
Expand All @@ -81,7 +87,8 @@ final class SystemManager {
requestLevel: .headers
) { message in
self.db.logger.debug(message, tag: "SyncRequest")
}
},
urlSession: session
)
)
)
Expand Down
8 changes: 4 additions & 4 deletions Sources/PowerSync/Implementation/PowerSyncDatabaseImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ final class PowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
let group: ActiveDatabaseGroup
let syncStatus = SwiftSyncStatus()
private let dbFilename: String?
private let httpClient: HttpClient
private let customHttpClient: HttpClient?
private let initializer = DatabaseInitializationAction()
let pool: any SQLiteConnectionPoolProtocol
let schema: AsyncMutex<Schema>
Expand All @@ -17,13 +17,13 @@ final class PowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
activeInstanceStore: DatabaseGroupCollection = .shared,
logger: any LoggerProtocol,
pool: any SQLiteConnectionPoolProtocol,
httpClient: HttpClient,
customHttpClient: HttpClient?,
schema: Schema
) {
self.dbFilename = dbFilename
self.logger = logger
self.schema = AsyncMutex(schema)
self.httpClient = httpClient
self.customHttpClient = customHttpClient
self.pool = pool
self.group = activeInstanceStore.referenceGroup(identifier: identifier, logger: logger)
}
Expand Down Expand Up @@ -133,7 +133,7 @@ final class PowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {

func connect(connector: any PowerSyncBackendConnectorProtocol, options: ConnectOptions?) async throws {
try await initialize()
await group.syncCoordinator.connect(db: self, connector: connector, options: options ?? ConnectOptions(), client: httpClient)
await group.syncCoordinator.connect(db: self, connector: connector, options: options ?? ConnectOptions(), client: customHttpClient)
}

func disconnectAndClear(clearLocal: Bool, soft: Bool) async throws {
Expand Down
2 changes: 0 additions & 2 deletions Sources/PowerSync/Implementation/sync/HttpClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ struct PlatformHttpClient: HttpClient {
let (data, response) = try await session.data(for: request)
return (response as! HTTPURLResponse, data)
}

static let shared = PlatformHttpClient(session: .shared)
}

/// A wrapper around a ``HttpClient`` emitting log events for responses and sync lines.
Expand Down
9 changes: 7 additions & 2 deletions Sources/PowerSync/Implementation/sync/SyncCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ actor SyncCoordinator {
nonisolated let streams = StreamTracker()
private var activeSync: Task<Void, any Error>?

func connect(db: PowerSyncDatabaseImpl, connector: PowerSyncBackendConnectorProtocol, options: ConnectOptions, client: HttpClient) async {
func connect(db: PowerSyncDatabaseImpl, connector: PowerSyncBackendConnectorProtocol, options: ConnectOptions, client: HttpClient?) async {
if let task = activeSync {
await self.finishSyncTask(task: task)
}

func defaultHttpClient() -> HttpClient {
let session = options.clientConfiguration?.urlSession ?? .shared
return PlatformHttpClient(session: session)
}

var client = client
var client = client ?? defaultHttpClient()
if let logger = options.clientConfiguration?.requestLogger {
client = LoggingClient(inner: client, logger: logger)
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/PowerSync/PowerSyncDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public func PowerSyncDatabase(
activeInstanceStore: group,
logger: logger,
pool: pool,
httpClient: PlatformHttpClient.shared,
customHttpClient: nil,
schema: schema
)
}
Expand Down Expand Up @@ -66,7 +66,7 @@ public func OpenedPowerSyncDatabase(
identifier: identifier,
logger: logger,
pool: pool,
httpClient: PlatformHttpClient.shared,
customHttpClient: nil,
schema: schema
)
}
15 changes: 13 additions & 2 deletions Sources/PowerSync/Protocol/PowerSyncDatabaseProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,21 @@ public struct SyncClientConfiguration: Sendable {
/// - SeeAlso: `SyncRequestLoggerConfiguration` for configuration options
public let requestLogger: SyncRequestLoggerConfiguration?

/// The URL session to use when connecting to the PowerSync service.
///
/// Customizing this may be convenient to add additional headers or to otherwise alter requests
/// sent by the PowerSync Swift SDK.
public var urlSession: URLSession

/// Creates a new sync client configuration.
/// - Parameter requestLogger: Optional network logger configuration
public init(requestLogger: SyncRequestLoggerConfiguration? = nil) {
/// - Parameter urlSession: Optional custom URL session to use for network requests.
public init(
requestLogger: SyncRequestLoggerConfiguration? = nil,
urlSession: URLSession = .shared,
) {
self.requestLogger = requestLogger
self.urlSession = urlSession
}
}

Expand Down Expand Up @@ -101,7 +112,7 @@ public struct ConnectOptions: Sendable {
params: JsonParam = [:],
clientConfiguration: SyncClientConfiguration? = nil,
appMetadata: [String: String] = [:],
includeDefaultStreams: Bool = true
includeDefaultStreams: Bool = true,
) {
self.crudThrottle = crudThrottle
self.retryDelay = retryDelay
Expand Down
8 changes: 4 additions & 4 deletions Tests/PowerSyncTests/Kotlin/ActiveInstanceStoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ struct MultipleInstanceTest {
let logger = DefaultLogger(minSeverity: .warning, writers: [logWriter])
let schema = Schema()

let a = PowerSyncDatabaseImpl(identifier: "id", logger: logger, pool: pool, httpClient: PlatformHttpClient.shared, schema: schema)
let a = PowerSyncDatabaseImpl(identifier: "id", logger: logger, pool: pool, customHttpClient: nil, schema: schema)
try #require(logWriter.getLogs().isEmpty)

let b = PowerSyncDatabaseImpl(identifier: "id", logger: logger, pool: pool, httpClient: PlatformHttpClient.shared, schema: schema)
let b = PowerSyncDatabaseImpl(identifier: "id", logger: logger, pool: pool, customHttpClient: nil, schema: schema)
let _ = try #require(logWriter.getLogs().first { $0.contains("Multiple PowerSync instances for the same database have been detected.") })

// Ensure databases are kept around until the end of the test (if a gets closed before, we would't see the warning).
Expand All @@ -27,10 +27,10 @@ struct MultipleInstanceTest {
let schema = Schema()

do {
let _ = PowerSyncDatabaseImpl(identifier: "id2", logger: logger, pool: pool, httpClient: PlatformHttpClient.shared, schema: schema)
let _ = PowerSyncDatabaseImpl(identifier: "id2", logger: logger, pool: pool, customHttpClient: nil, schema: schema)
}

let b = PowerSyncDatabaseImpl(identifier: "id2", logger: logger, pool: pool, httpClient: PlatformHttpClient.shared, schema: schema)
let b = PowerSyncDatabaseImpl(identifier: "id2", logger: logger, pool: pool, customHttpClient: nil, schema: schema)
try #require(logWriter.getLogs().isEmpty)
let _ = consume b
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/PowerSyncTests/Kotlin/SqlCursorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ final class SqlCursorTests: XCTestCase {
activeInstanceStore: DatabaseGroupCollection(),
logger: DefaultLogger(),
pool: AsyncConnectionPool(location: .inMemory, logger: DefaultLogger()),
httpClient: PlatformHttpClient.shared,
customHttpClient: nil,
schema: schema,
)
try await database.disconnectAndClear()
Expand Down
4 changes: 2 additions & 2 deletions Tests/PowerSyncTests/SyncTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ class InMemorySyncIntegrationTests {
}

@Test func subscriptionsUpdateWhileOffline() async throws {
let db = openDatabase(PlatformHttpClient.shared)
let db = openDatabase(PlatformHttpClient(session: .shared))
// Make sure the database is initialized
try await db.readLock { _ in }
var statusUpdates = db.currentStatus.asFlow().makeAsyncIterator()
Expand Down Expand Up @@ -823,7 +823,7 @@ private func openDatabase(_ client: any HttpClient, schema: Schema = defaultSche
activeInstanceStore: DatabaseGroupCollection(),
logger: logger,
pool: AsyncConnectionPool(location: .inMemory, logger: DefaultLogger()),
httpClient: client,
customHttpClient: client,
schema: schema,
)
}
Expand Down
Loading