Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift 6 concurrency support #33

Merged
merged 5 commits into from
Oct 29, 2024
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
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
72 changes: 42 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
ncooke3 marked this conversation as resolved.
Show resolved Hide resolved

@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,26 @@ 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
)
grpcClient = GrpcClient(
app: app,
settings: settings,
connectorConfig: connectorConfig,
callerSDKType: callerSDKType
)

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

// MARK: Init
Expand All @@ -101,8 +111,6 @@ public class DataConnect {
app: self.app,
settings: settings,
connectorConfig: connectorConfig,
auth: Auth.auth(app: app),
appCheck: AppCheck.appCheck(app: app),
callerSDKType: self.callerSDKType
)
operationsManager = OperationsManager(grpcClient: grpcClient)
Expand All @@ -111,33 +119,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 +182,7 @@ private class InstanceStore {
autoreleaseFrequency: .workItem
)

var instances = [InstanceKey: DataConnect]()
private var instances = [InstanceKey: DataConnect]()
ncooke3 marked this conversation as resolved.
Show resolved Hide resolved

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
10 changes: 4 additions & 6 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 Expand Up @@ -108,8 +108,6 @@ actor GrpcClient: CustomStringConvertible {
}()

init(app: FirebaseApp, settings: DataConnectSettings, connectorConfig: ConnectorConfig,
auth: Auth,
appCheck: AppCheck?,
callerSDKType: CallerSDKType) {
self.app = app

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

serverSettings = settings
self.connectorConfig = connectorConfig
self.auth = auth
self.appCheck = appCheck
auth = Auth.auth(app: app)
appCheck = AppCheck.appCheck(app: app)
self.callerSDKType = callerSDKType

connectorName =
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
3 changes: 3 additions & 0 deletions Sources/Scalars/AnyValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,6 @@ extension AnyValue: Hashable {
hasher.combine(value)
}
}

@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
extension AnyValue: Sendable {}
2 changes: 1 addition & 1 deletion Sources/Scalars/LocalDate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import Foundation
Essentially represents: https://the-guild.dev/graphql/scalars/docs/scalars/local-date
*/
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
public struct LocalDate: Codable, Equatable, Comparable, CustomStringConvertible {
public struct LocalDate: Codable, Comparable, CustomStringConvertible, Equatable, Sendable {
private var calendar = Calendar(identifier: .gregorian)
private var dateFormatter = DateFormatter()
private var date = Date()
Expand Down
Loading