1414
1515import 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 , * )
2222public 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 {
0 commit comments