Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
8 changes: 4 additions & 4 deletions Sources/BaseOperationRef.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@
import Foundation

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public struct OperationResult<ResultData: Decodable> {
public struct OperationResult<ResultData: Decodable & Sendable>: Sendable {
public var data: ResultData
}

// notional protocol that denotes a variable.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public protocol OperationVariable: Encodable, Hashable, Equatable {}
public protocol OperationVariable: Encodable, Hashable, Equatable, Sendable {}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
protocol OperationRequest: Hashable, Equatable {
protocol OperationRequest: Hashable, Equatable, Sendable {
associatedtype Variable: OperationVariable
var operationName: String { get } // Name within Connector definition
var variables: Variable? { get }
}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public protocol OperationRef {
associatedtype ResultData: Decodable
associatedtype ResultData: Decodable & Sendable

func execute() async throws -> OperationResult<ResultData>
}
2 changes: 1 addition & 1 deletion Sources/ConnectorConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import Foundation

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public struct ConnectorConfig: Hashable, Equatable {
public struct ConnectorConfig: Hashable, Equatable, Sendable {
public private(set) var serviceId: String
public private(set) var location: String
public private(set) var connector: String
Expand Down
80 changes: 50 additions & 30 deletions Sources/DataConnect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@

import Foundation

import FirebaseAppCheck
import FirebaseAuth
import FirebaseCore
@preconcurrency import FirebaseAppCheck
@preconcurrency import FirebaseAuth
@preconcurrency import FirebaseCore

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public class DataConnect {
Expand All @@ -29,7 +29,17 @@ public class DataConnect {

private var callerSDKType: CallerSDKType = .base

private static var instanceStore = InstanceStore()
private let accessQueue = DispatchQueue(
label: "com.google.firebase.dataConnect.instanceAccessQueue",
autoreleaseFrequency: .workItem
)

// Instance store uses an internal queue to protect mutable state.
#if compiler(>=6)
private nonisolated(unsafe) static let instanceStore = InstanceStore()
#else
private static let instanceStore = InstanceStore()
#endif

public enum EmulatorDefaults {
public static let host = "127.0.0.1"
Expand Down Expand Up @@ -62,26 +72,30 @@ public class DataConnect {

public func useEmulator(host: String = EmulatorDefaults.host,
port: Int = EmulatorDefaults.port) {
settings = DataConnectSettings(host: host, port: port, sslEnabled: false)
accessQueue.sync {
settings = DataConnectSettings(host: host, port: port, sslEnabled: false)

// TODO: - shutdown grpc client
// self.grpcClient.close
// self.operations.close
// TODO: - shutdown grpc client
// self.grpcClient.close
// self.operations.close

guard app.options.projectID != nil else {
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
}
guard app.options.projectID != nil else {
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
}

grpcClient = GrpcClient(
app: app,
settings: settings,
connectorConfig: connectorConfig,
auth: Auth.auth(app: app),
appCheck: AppCheck.appCheck(app: app),
callerSDKType: callerSDKType
)
let auth = Auth.auth(app: app)
nonisolated(unsafe) let appCheck = AppCheck.appCheck(app: app)
grpcClient = GrpcClient(
app: app,
settings: settings,
connectorConfig: connectorConfig,
auth: auth,
appCheck: appCheck,
callerSDKType: callerSDKType
)

operationsManager = OperationsManager(grpcClient: grpcClient)
operationsManager = OperationsManager(grpcClient: grpcClient)
}
}

// MARK: Init
Expand All @@ -97,12 +111,14 @@ public class DataConnect {
self.connectorConfig = connectorConfig
self.callerSDKType = callerSDKType

let auth = Auth.auth(app: app)
nonisolated(unsafe) let appCheck = AppCheck.appCheck(app: app)
grpcClient = GrpcClient(
app: self.app,
settings: settings,
connectorConfig: connectorConfig,
auth: Auth.auth(app: app),
appCheck: AppCheck.appCheck(app: app),
auth: auth,
appCheck: appCheck,
callerSDKType: self.callerSDKType
)
operationsManager = OperationsManager(grpcClient: grpcClient)
Expand All @@ -111,33 +127,37 @@ public class DataConnect {
// MARK: Operations

/// Returns a query ref matching the name and variables.
public func query<ResultData: Decodable,
public func query<ResultData: Decodable & Sendable,
Variable: OperationVariable>(name: String,
variables: Variable,
resultsDataType: ResultData
.Type,
publisher: ResultsPublisherType = .observableObject)
-> any ObservableQueryRef {
let request = QueryRequest(operationName: name, variables: variables)
return operationsManager.queryRef(for: request, with: resultsDataType, publisher: publisher)
accessQueue.sync {
let request = QueryRequest(operationName: name, variables: variables)
return operationsManager.queryRef(for: request, with: resultsDataType, publisher: publisher)
}
}

/// Returns a Mutation Ref matching the name and specified variables.
public func mutation<ResultData: Decodable,
public func mutation<ResultData: Decodable & Sendable,
Variable: OperationVariable>(name: String,
variables: Variable,
resultsDataType: ResultData
.Type)
-> MutationRef<ResultData,
Variable> {
let request = MutationRequest(operationName: name, variables: variables)
return operationsManager.mutationRef(for: request, with: resultsDataType)
accessQueue.sync {
let request = MutationRequest(operationName: name, variables: variables)
return operationsManager.mutationRef(for: request, with: resultsDataType)
}
}
}

// This enum is public so the gen sdk can access it
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public enum CallerSDKType {
public enum CallerSDKType: Sendable {
case base // base sdk is directly used
case generated // generated sdk is calling the base
}
Expand Down Expand Up @@ -170,7 +190,7 @@ private class InstanceStore {
autoreleaseFrequency: .workItem
)

var instances = [InstanceKey: DataConnect]()
private var instances = [InstanceKey: DataConnect]()

func instance(for app: FirebaseApp, config: ConnectorConfig,
settings: DataConnectSettings, callerSDKType: CallerSDKType) -> DataConnect {
Expand Down
2 changes: 1 addition & 1 deletion Sources/DataConnectSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import Foundation

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public struct DataConnectSettings: Hashable, Equatable {
public struct DataConnectSettings: Hashable, Equatable, Sendable {
public private(set) var host: String
public private(set) var port: Int
public private(set) var sslEnabled: Bool
Expand Down
4 changes: 2 additions & 2 deletions Sources/Internal/GrpcClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

import Foundation

import FirebaseAppCheck
import FirebaseAuth
@preconcurrency import FirebaseAppCheck
@preconcurrency import FirebaseAuth
import FirebaseCore
import GRPC
import NIOCore
Expand Down
2 changes: 1 addition & 1 deletion Sources/Internal/OperationsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class OperationsManager {
self.grpcClient = grpcClient
}

func queryRef<ResultDataType: Decodable,
func queryRef<ResultDataType: Decodable & Sendable,
VariableType: OperationVariable>(for request: QueryRequest<VariableType>,
with resultType: ResultDataType
.Type,
Expand Down
5 changes: 4 additions & 1 deletion Sources/MutationRef.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ struct MutationRequest<Variable: OperationVariable>: OperationRequest {

/// Represents a predefined graphql mutation identified by name and variables.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public class MutationRef<ResultData: Decodable, Variable: OperationVariable>: OperationRef {
public class MutationRef<
ResultData: Decodable & Sendable,
Variable: OperationVariable
>: OperationRef {
private var request: any OperationRequest

private var grpcClient: GrpcClient
Expand Down
14 changes: 7 additions & 7 deletions Sources/QueryRef.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import Foundation

import Combine
@preconcurrency import Combine
import Observation

/// The type of publisher to use for the Query Ref
Expand Down Expand Up @@ -75,13 +75,13 @@ public protocol QueryRef: OperationRef {
}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
actor GenericQueryRef<ResultData: Decodable, Variable: OperationVariable>: QueryRef {
private var resultsPublisher = PassthroughSubject<Result<ResultData, DataConnectError>,
actor GenericQueryRef<ResultData: Decodable & Sendable, Variable: OperationVariable>: QueryRef {
private let resultsPublisher = PassthroughSubject<Result<ResultData, DataConnectError>,
Never>()

private var request: QueryRequest<Variable>
private let request: QueryRequest<Variable>

private var grpcClient: GrpcClient
private let grpcClient: GrpcClient

init(request: QueryRequest<Variable>, grpcClient: GrpcClient) {
self.request = request
Expand Down Expand Up @@ -143,7 +143,7 @@ public protocol ObservableQueryRef: QueryRef {
/// If last fetch was successful, this variable is cleared
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public class QueryRefObservableObject<
ResultData: Decodable,
ResultData: Decodable & Sendable,
Variable: OperationVariable
>: ObservableObject, ObservableQueryRef {
private var request: QueryRequest<Variable>
Expand Down Expand Up @@ -216,7 +216,7 @@ public class QueryRefObservableObject<
@available(macOS 14, iOS 17, tvOS 17, watchOS 10, *)
@Observable
public class QueryRefObservation<
ResultData: Decodable,
ResultData: Decodable & Sendable,
Variable: OperationVariable
>: ObservableQueryRef {
@ObservationIgnored
Expand Down
Loading