diff --git a/Sources/OpenFeature/OpenFeatureClient.swift b/Sources/OpenFeature/OpenFeatureClient.swift index b2ed540..1adb3dd 100644 --- a/Sources/OpenFeature/OpenFeatureClient.swift +++ b/Sources/OpenFeature/OpenFeatureClient.swift @@ -2,23 +2,14 @@ import Foundation import os public class OpenFeatureClient: Client { - // TODO: We use DispatchQueue here instead of being an actor to not lock into new versions of Swift - private let hookQueue = DispatchQueue(label: "dev.openfeature.client.hook") - private let contextQueue = DispatchQueue(label: "dev.openfeature.client.context") + private let hookLock = NSLock() private var openFeatureApi: OpenFeatureAPI private(set) var name: String? private(set) var version: String? - private var _metadata: Metadata - public var metadata: Metadata { - return _metadata - } - - private var _hooks: [any Hook] = [] - public var hooks: [any Hook] { - return _hooks - } + private(set) public var metadata: Metadata + private(set) public var hooks: [any Hook] = [] private var hookSupport = HookSupport() private var logger = Logger() @@ -27,13 +18,13 @@ public class OpenFeatureClient: Client { self.openFeatureApi = openFeatureApi self.name = name self.version = version - self._metadata = ClientMetadata(name: name) + self.metadata = ClientMetadata(name: name) } public func addHooks(_ hooks: any Hook...) { - self.hookQueue.sync { - self._hooks.append(contentsOf: hooks) - } + hookLock.lock() + self.hooks.append(contentsOf: hooks) + hookLock.unlock() } } @@ -58,7 +49,6 @@ extension OpenFeatureClient { -> FlagEvaluationDetails where T: AllowedFlagValueType, T: Equatable { evaluateFlag( - flagValueType: T.flagValueType, key: key, defaultValue: defaultValue, options: options @@ -73,8 +63,7 @@ extension OpenFeatureClient { } extension OpenFeatureClient { - private func evaluateFlag( - flagValueType: FlagValueType, + private func evaluateFlag( key: String, defaultValue: T, options: FlagEvaluationOptions? @@ -85,20 +74,22 @@ extension OpenFeatureClient { var details = FlagEvaluationDetails(flagKey: key, value: defaultValue) let provider = openFeatureApi.getProvider() ?? NoOpProvider() - let mergedHooks = provider.hooks + options.hooks + hooks + openFeatureApi.hooks let hookCtx = HookContext( flagKey: key, - type: flagValueType, + type: T.flagValueType, defaultValue: defaultValue, ctx: context, clientMetadata: self.metadata, providerMetadata: provider.metadata) + hookLock.lock() + let mergedHooks = provider.hooks + options.hooks + hooks + openFeatureApi.hooks + hookLock.unlock() + do { - hookSupport.beforeHooks(flagValueType: flagValueType, hookCtx: hookCtx, hooks: mergedHooks, hints: hints) + hookSupport.beforeHooks(flagValueType: T.flagValueType, hookCtx: hookCtx, hooks: mergedHooks, hints: hints) let providerEval = try createProviderEvaluation( - flagValueType: flagValueType, key: key, context: context, defaultValue: defaultValue, @@ -108,7 +99,7 @@ extension OpenFeatureClient { details = evalDetails try hookSupport.afterHooks( - flagValueType: flagValueType, hookCtx: hookCtx, details: evalDetails, hooks: mergedHooks, hints: hints) + flagValueType: T.flagValueType, hookCtx: hookCtx, details: evalDetails, hooks: mergedHooks, hints: hints) } catch { logger.error("Unable to correctly evaluate flag with key \(key) due to exception \(error)") @@ -122,24 +113,23 @@ extension OpenFeatureClient { details.reason = Reason.error.rawValue hookSupport.errorHooks( - flagValueType: flagValueType, hookCtx: hookCtx, error: error, hooks: mergedHooks, hints: hints) + flagValueType: T.flagValueType, hookCtx: hookCtx, error: error, hooks: mergedHooks, hints: hints) } hookSupport.afterAllHooks( - flagValueType: flagValueType, hookCtx: hookCtx, hooks: mergedHooks, hints: hints) + flagValueType: T.flagValueType, hookCtx: hookCtx, hooks: mergedHooks, hints: hints) return details } // swiftlint:disable:next cyclomatic_complexity - private func createProviderEvaluation( - flagValueType: FlagValueType, + private func createProviderEvaluation( key: String, context: EvaluationContext?, defaultValue: V, provider: FeatureProvider ) throws -> ProviderEvaluation { - switch flagValueType { + switch V.flagValueType { case .boolean: guard let defaultValue = defaultValue as? Bool else { break