Skip to content

Commit 13f249b

Browse files
Swift 6 concurrency support (#33)
1 parent 8656023 commit 13f249b

File tree

10 files changed

+68
-52
lines changed

10 files changed

+68
-52
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: 42 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,17 @@ 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+
37+
// Instance store uses an internal queue to protect mutable state.
38+
#if compiler(>=6)
39+
private nonisolated(unsafe) static let instanceStore = InstanceStore()
40+
#else
41+
private static let instanceStore = InstanceStore()
42+
#endif
3343

3444
public enum EmulatorDefaults {
3545
public static let host = "127.0.0.1"
@@ -62,26 +72,26 @@ public class DataConnect {
6272

6373
public func useEmulator(host: String = EmulatorDefaults.host,
6474
port: Int = EmulatorDefaults.port) {
65-
settings = DataConnectSettings(host: host, port: port, sslEnabled: false)
75+
accessQueue.sync {
76+
settings = DataConnectSettings(host: host, port: port, sslEnabled: false)
6677

67-
// TODO: - shutdown grpc client
68-
// self.grpcClient.close
69-
// self.operations.close
78+
// TODO: - shutdown grpc client
79+
// self.grpcClient.close
80+
// self.operations.close
7081

71-
guard app.options.projectID != nil else {
72-
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
73-
}
82+
guard app.options.projectID != nil else {
83+
fatalError("Firebase DataConnect requires the projectID to be set in the app options")
84+
}
7485

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-
)
86+
grpcClient = GrpcClient(
87+
app: app,
88+
settings: settings,
89+
connectorConfig: connectorConfig,
90+
callerSDKType: callerSDKType
91+
)
8392

84-
operationsManager = OperationsManager(grpcClient: grpcClient)
93+
operationsManager = OperationsManager(grpcClient: grpcClient)
94+
}
8595
}
8696

8797
// MARK: Init
@@ -101,8 +111,6 @@ public class DataConnect {
101111
app: self.app,
102112
settings: settings,
103113
connectorConfig: connectorConfig,
104-
auth: Auth.auth(app: app),
105-
appCheck: AppCheck.appCheck(app: app),
106114
callerSDKType: self.callerSDKType
107115
)
108116
operationsManager = OperationsManager(grpcClient: grpcClient)
@@ -111,33 +119,37 @@ public class DataConnect {
111119
// MARK: Operations
112120

113121
/// Returns a query ref matching the name and variables.
114-
public func query<ResultData: Decodable,
122+
public func query<ResultData: Decodable & Sendable,
115123
Variable: OperationVariable>(name: String,
116124
variables: Variable,
117125
resultsDataType: ResultData
118126
.Type,
119127
publisher: ResultsPublisherType = .observableObject)
120128
-> any ObservableQueryRef {
121-
let request = QueryRequest(operationName: name, variables: variables)
122-
return operationsManager.queryRef(for: request, with: resultsDataType, publisher: publisher)
129+
accessQueue.sync {
130+
let request = QueryRequest(operationName: name, variables: variables)
131+
return operationsManager.queryRef(for: request, with: resultsDataType, publisher: publisher)
132+
}
123133
}
124134

125135
/// Returns a Mutation Ref matching the name and specified variables.
126-
public func mutation<ResultData: Decodable,
136+
public func mutation<ResultData: Decodable & Sendable,
127137
Variable: OperationVariable>(name: String,
128138
variables: Variable,
129139
resultsDataType: ResultData
130140
.Type)
131141
-> MutationRef<ResultData,
132142
Variable> {
133-
let request = MutationRequest(operationName: name, variables: variables)
134-
return operationsManager.mutationRef(for: request, with: resultsDataType)
143+
accessQueue.sync {
144+
let request = MutationRequest(operationName: name, variables: variables)
145+
return operationsManager.mutationRef(for: request, with: resultsDataType)
146+
}
135147
}
136148
}
137149

138150
// This enum is public so the gen sdk can access it
139151
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
140-
public enum CallerSDKType {
152+
public enum CallerSDKType: Sendable {
141153
case base // base sdk is directly used
142154
case generated // generated sdk is calling the base
143155
}
@@ -170,7 +182,7 @@ private class InstanceStore {
170182
autoreleaseFrequency: .workItem
171183
)
172184

173-
var instances = [InstanceKey: DataConnect]()
185+
private var instances = [InstanceKey: DataConnect]()
174186

175187
func instance(for app: FirebaseApp, config: ConnectorConfig,
176188
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: 4 additions & 6 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
@@ -108,8 +108,6 @@ actor GrpcClient: CustomStringConvertible {
108108
}()
109109

110110
init(app: FirebaseApp, settings: DataConnectSettings, connectorConfig: ConnectorConfig,
111-
auth: Auth,
112-
appCheck: AppCheck?,
113111
callerSDKType: CallerSDKType) {
114112
self.app = app
115113

@@ -120,8 +118,8 @@ actor GrpcClient: CustomStringConvertible {
120118

121119
serverSettings = settings
122120
self.connectorConfig = connectorConfig
123-
self.auth = auth
124-
self.appCheck = appCheck
121+
auth = Auth.auth(app: app)
122+
appCheck = AppCheck.appCheck(app: app)
125123
self.callerSDKType = callerSDKType
126124

127125
connectorName =

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: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ 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<
31+
ResultData: Decodable & Sendable,
32+
Variable: OperationVariable
33+
>: OperationRef {
3134
private var request: any OperationRequest
3235

3336
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

Sources/Scalars/AnyValue.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,6 @@ extension AnyValue: Hashable {
6262
hasher.combine(value)
6363
}
6464
}
65+
66+
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
67+
extension AnyValue: Sendable {}

Sources/Scalars/LocalDate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import Foundation
2020
Essentially represents: https://the-guild.dev/graphql/scalars/docs/scalars/local-date
2121
*/
2222
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
23-
public struct LocalDate: Codable, Equatable, Comparable, CustomStringConvertible {
23+
public struct LocalDate: Codable, Comparable, CustomStringConvertible, Equatable, Sendable {
2424
private var calendar = Calendar(identifier: .gregorian)
2525
private var dateFormatter = DateFormatter()
2626
private var date = Date()

0 commit comments

Comments
 (0)