Skip to content

Commit 286c12e

Browse files
Mark types as Sendable
1 parent 8656023 commit 286c12e

File tree

8 files changed

+64
-47
lines changed

8 files changed

+64
-47
lines changed

Sources/BaseOperationRef.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,24 @@
1515
import Foundation
1616

1717
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
18-
public struct OperationResult<ResultData: Decodable> {
18+
public struct OperationResult<ResultData: Decodable & Sendable>: Sendable {
1919
public var data: ResultData
2020
}
2121

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

2626
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
27-
protocol OperationRequest: Hashable, Equatable {
27+
protocol OperationRequest: Hashable, Equatable, Sendable {
2828
associatedtype Variable: OperationVariable
2929
var operationName: String { get } // Name within Connector definition
3030
var variables: Variable? { get }
3131
}
3232

3333
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
3434
public protocol OperationRef {
35-
associatedtype ResultData: Decodable
35+
associatedtype ResultData: (Decodable & Sendable)
3636

3737
func execute() async throws -> OperationResult<ResultData>
3838
}

Sources/ConnectorConfig.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import Foundation
1616

1717
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
18-
public struct ConnectorConfig: Hashable, Equatable {
18+
public struct ConnectorConfig: Hashable, Equatable, Sendable {
1919
public private(set) var serviceId: String
2020
public private(set) var location: String
2121
public private(set) var connector: String

Sources/DataConnect.swift

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
import Foundation
1616

17-
import FirebaseAppCheck
18-
import FirebaseAuth
19-
import FirebaseCore
17+
@preconcurrency import FirebaseAppCheck
18+
@preconcurrency import FirebaseAuth
19+
@preconcurrency import FirebaseCore
2020

2121
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
2222
public class DataConnect {
@@ -29,7 +29,13 @@ public class DataConnect {
2929

3030
private var callerSDKType: CallerSDKType = .base
3131

32-
private static var instanceStore = InstanceStore()
32+
private let accessQueue = DispatchQueue(
33+
label: "com.google.firebase.dataConnect.instanceAccessQueue",
34+
autoreleaseFrequency: .workItem)
35+
36+
// Instance store uses an internal queue to protect mutable state.
37+
// So marking it as
38+
nonisolated(unsafe) private static let instanceStore = InstanceStore()
3339

3440
public enum EmulatorDefaults {
3541
public static let host = "127.0.0.1"
@@ -62,26 +68,31 @@ public class DataConnect {
6268

6369
public func useEmulator(host: String = EmulatorDefaults.host,
6470
port: Int = EmulatorDefaults.port) {
65-
settings = DataConnectSettings(host: host, port: port, sslEnabled: false)
6671

67-
// TODO: - shutdown grpc client
68-
// self.grpcClient.close
69-
// self.operations.close
72+
accessQueue.sync {
73+
settings = DataConnectSettings(host: host, port: port, sslEnabled: false)
7074

71-
guard app.options.projectID != nil else {
72-
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
73-
}
75+
// TODO: - shutdown grpc client
76+
// self.grpcClient.close
77+
// self.operations.close
7478

75-
grpcClient = GrpcClient(
76-
app: app,
77-
settings: settings,
78-
connectorConfig: connectorConfig,
79-
auth: Auth.auth(app: app),
80-
appCheck: AppCheck.appCheck(app: app),
81-
callerSDKType: callerSDKType
82-
)
79+
guard app.options.projectID != nil else {
80+
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
81+
}
8382

84-
operationsManager = OperationsManager(grpcClient: grpcClient)
83+
let auth = Auth.auth(app: app)
84+
nonisolated(unsafe) let appCheck = AppCheck.appCheck(app: app)
85+
grpcClient = GrpcClient(
86+
app: app,
87+
settings: settings,
88+
connectorConfig: connectorConfig,
89+
auth: auth,
90+
appCheck: appCheck,
91+
callerSDKType: callerSDKType
92+
)
93+
94+
operationsManager = OperationsManager(grpcClient: grpcClient)
95+
}
8596
}
8697

8798
// MARK: Init
@@ -97,12 +108,14 @@ public class DataConnect {
97108
self.connectorConfig = connectorConfig
98109
self.callerSDKType = callerSDKType
99110

111+
let auth = Auth.auth(app: app)
112+
nonisolated(unsafe) let appCheck = AppCheck.appCheck(app: app)
100113
grpcClient = GrpcClient(
101114
app: self.app,
102115
settings: settings,
103116
connectorConfig: connectorConfig,
104-
auth: Auth.auth(app: app),
105-
appCheck: AppCheck.appCheck(app: app),
117+
auth: auth,
118+
appCheck: appCheck,
106119
callerSDKType: self.callerSDKType
107120
)
108121
operationsManager = OperationsManager(grpcClient: grpcClient)
@@ -111,33 +124,37 @@ public class DataConnect {
111124
// MARK: Operations
112125

113126
/// Returns a query ref matching the name and variables.
114-
public func query<ResultData: Decodable,
127+
public func query<ResultData: Decodable & Sendable,
115128
Variable: OperationVariable>(name: String,
116129
variables: Variable,
117130
resultsDataType: ResultData
118131
.Type,
119132
publisher: ResultsPublisherType = .observableObject)
120133
-> any ObservableQueryRef {
121-
let request = QueryRequest(operationName: name, variables: variables)
122-
return operationsManager.queryRef(for: request, with: resultsDataType, publisher: publisher)
134+
accessQueue.sync {
135+
let request = QueryRequest(operationName: name, variables: variables)
136+
return operationsManager.queryRef(for: request, with: resultsDataType, publisher: publisher)
137+
}
123138
}
124139

125140
/// Returns a Mutation Ref matching the name and specified variables.
126-
public func mutation<ResultData: Decodable,
141+
public func mutation<ResultData: Decodable & Sendable,
127142
Variable: OperationVariable>(name: String,
128143
variables: Variable,
129144
resultsDataType: ResultData
130145
.Type)
131146
-> MutationRef<ResultData,
132147
Variable> {
133-
let request = MutationRequest(operationName: name, variables: variables)
134-
return operationsManager.mutationRef(for: request, with: resultsDataType)
148+
accessQueue.sync {
149+
let request = MutationRequest(operationName: name, variables: variables)
150+
return operationsManager.mutationRef(for: request, with: resultsDataType)
151+
}
135152
}
136153
}
137154

138155
// This enum is public so the gen sdk can access it
139156
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
140-
public enum CallerSDKType {
157+
public enum CallerSDKType: Sendable {
141158
case base // base sdk is directly used
142159
case generated // generated sdk is calling the base
143160
}
@@ -170,7 +187,7 @@ private class InstanceStore {
170187
autoreleaseFrequency: .workItem
171188
)
172189

173-
var instances = [InstanceKey: DataConnect]()
190+
private var instances = [InstanceKey: DataConnect]()
174191

175192
func instance(for app: FirebaseApp, config: ConnectorConfig,
176193
settings: DataConnectSettings, callerSDKType: CallerSDKType) -> DataConnect {

Sources/DataConnectSettings.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import Foundation
1616

1717
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
18-
public struct DataConnectSettings: Hashable, Equatable {
18+
public struct DataConnectSettings: Hashable, Equatable, Sendable {
1919
public private(set) var host: String
2020
public private(set) var port: Int
2121
public private(set) var sslEnabled: Bool

Sources/Internal/GrpcClient.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515
import Foundation
1616

17-
import FirebaseAppCheck
18-
import FirebaseAuth
17+
@preconcurrency import FirebaseAppCheck
18+
@preconcurrency import FirebaseAuth
1919
import FirebaseCore
2020
import GRPC
2121
import NIOCore

Sources/Internal/OperationsManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class OperationsManager {
3434
self.grpcClient = grpcClient
3535
}
3636

37-
func queryRef<ResultDataType: Decodable,
37+
func queryRef<ResultDataType: Decodable & Sendable,
3838
VariableType: OperationVariable>(for request: QueryRequest<VariableType>,
3939
with resultType: ResultDataType
4040
.Type,

Sources/MutationRef.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct MutationRequest<Variable: OperationVariable>: OperationRequest {
2727

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

3333
private var grpcClient: GrpcClient

Sources/QueryRef.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import Foundation
1616

17-
import Combine
17+
@preconcurrency import Combine
1818
import Observation
1919

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

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

82-
private var request: QueryRequest<Variable>
82+
private let request: QueryRequest<Variable>
8383

84-
private var grpcClient: GrpcClient
84+
private let grpcClient: GrpcClient
8585

8686
init(request: QueryRequest<Variable>, grpcClient: GrpcClient) {
8787
self.request = request
@@ -143,7 +143,7 @@ public protocol ObservableQueryRef: QueryRef {
143143
/// If last fetch was successful, this variable is cleared
144144
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
145145
public class QueryRefObservableObject<
146-
ResultData: Decodable,
146+
ResultData: Decodable & Sendable,
147147
Variable: OperationVariable
148148
>: ObservableObject, ObservableQueryRef {
149149
private var request: QueryRequest<Variable>
@@ -216,7 +216,7 @@ public class QueryRefObservableObject<
216216
@available(macOS 14, iOS 17, tvOS 17, watchOS 10, *)
217217
@Observable
218218
public class QueryRefObservation<
219-
ResultData: Decodable,
219+
ResultData: Decodable & Sendable,
220220
Variable: OperationVariable
221221
>: ObservableQueryRef {
222222
@ObservationIgnored

0 commit comments

Comments
 (0)