Skip to content

Commit

Permalink
feat!: Add non await putcontext and awaitreconciliation
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziodemaria committed Dec 5, 2024
1 parent 9e79dfb commit 1a0b34a
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 80 deletions.
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ analyzer_rules:
- unused_import

opt_in_rules:
- attributes
- array_init
- closure_end_indentation
- closure_spacing
Expand Down
20 changes: 0 additions & 20 deletions ConfidenceDemoApp/ConfidenceDemoApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
/* Begin PBXBuildFile section */
733219BF2BE3C11100747AC2 /* ConfidenceOpenFeature in Frameworks */ = {isa = PBXBuildFile; productRef = 733219BE2BE3C11100747AC2 /* ConfidenceOpenFeature */; };
735EADF52CF9B64E007BC42C /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735EADF42CF9B64E007BC42C /* LoginView.swift */; };
7383EED62D01EE8300EC738D /* Confidence in Frameworks */ = {isa = PBXBuildFile; productRef = 7383EED52D01EE8300EC738D /* Confidence */; };
C770C99A2A739FBC00C2AC8C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C770C9962A739FBC00C2AC8C /* Preview Assets.xcassets */; };
C770C99B2A739FBC00C2AC8C /* ConfidenceDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C770C9972A739FBC00C2AC8C /* ConfidenceDemoApp.swift */; };
C770C99C2A739FBC00C2AC8C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C770C9982A739FBC00C2AC8C /* ContentView.swift */; };
Expand Down Expand Up @@ -57,7 +56,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
7383EED62D01EE8300EC738D /* Confidence in Frameworks */,
733219BF2BE3C11100747AC2 /* ConfidenceOpenFeature in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -172,7 +170,6 @@
name = ConfidenceDemoApp;
packageProductDependencies = (
733219BE2BE3C11100747AC2 /* ConfidenceOpenFeature */,
7383EED52D01EE8300EC738D /* Confidence */,
);
productName = ConfidenceDemoApp;
productReference = C770C9682A739FA000C2AC8C /* ConfidenceDemoApp.app */;
Expand Down Expand Up @@ -250,7 +247,6 @@
);
mainGroup = C770C95F2A739FA000C2AC8C;
packageReferences = (
7383EED42D01EE8300EC738D /* XCRemoteSwiftPackageReference "confidence-sdk-swift" */,
);
productRefGroup = C770C9692A739FA000C2AC8C /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -649,27 +645,11 @@
};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
7383EED42D01EE8300EC738D /* XCRemoteSwiftPackageReference "confidence-sdk-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "git@github.com:spotify/confidence-sdk-swift.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 1.1.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
733219BE2BE3C11100747AC2 /* ConfidenceOpenFeature */ = {
isa = XCSwiftPackageProductDependency;
productName = ConfidenceOpenFeature;
};
7383EED52D01EE8300EC738D /* Confidence */ = {
isa = XCSwiftPackageProductDependency;
package = 7383EED42D01EE8300EC738D /* XCRemoteSwiftPackageReference "confidence-sdk-swift" */;
productName = Confidence;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = C770C9602A739FA000C2AC8C /* Project object */;
Expand Down
2 changes: 1 addition & 1 deletion ConfidenceDemoApp/ConfidenceDemoApp/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct ContentView: View {
flaggingState.state = .loading
flaggingState.color = .gray
Task {
await confidence.removeContext(key: "user_id")
await confidence.removeContextAndWait(key: "user_id")
flaggingState.state = .ready
}
loggedOut = true
Expand Down
2 changes: 1 addition & 1 deletion ConfidenceDemoApp/ConfidenceDemoApp/LoginView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ struct LoginView: View {
flaggingState.state = .loading
try? await Task.sleep(nanoseconds: 5 * 1_000_000_000) // simulating network delay
// putContext adds the user_id field to the evaluation context and fetches values for it
await confidence.putContext(context: ["user_id": .init(string: "user1")])
await confidence.putContextAndWait(context: ["user_id": .init(string: "user1")])
flaggingState.state = .ready
}

Expand Down
110 changes: 75 additions & 35 deletions Sources/Confidence/Confidence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
import Combine
import os

// swiftlint:disable:next type_body_length
public class Confidence: ConfidenceEventSender {
// User configurations
private let clientSecret: String
Expand All @@ -22,7 +23,7 @@ public class Confidence: ConfidenceEventSender {
// Synchronization and task management resources
private var cancellables = Set<AnyCancellable>()
private let cacheQueue = DispatchQueue(label: "com.confidence.queue.cache")
private let semaphore = DispatchSemaphore(value: 1)
private var currentFetchTask: Task<(), Never>?

// Internal for testing
internal let remoteFlagResolver: ConfidenceResolveClient
Expand Down Expand Up @@ -153,11 +154,9 @@ public class Confidence: ConfidenceEventSender {
return contextManager.getContext(parentContext: parentContext)
}

public func putContext(key: String, value: ConfidenceValue) async {
await withSemaphoreAsync { [weak self] in
guard let self = self else {
return
}
public func putContextAndWait(key: String, value: ConfidenceValue) async {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
let newContext = contextManager.updateContext(withValues: [key: value], removedKeys: [])
do {
try await self.fetchAndActivate()
Expand All @@ -166,13 +165,12 @@ public class Confidence: ConfidenceEventSender {
debugLogger?.logMessage(message: "Error when putting context: \(error)", isWarning: true)
}
}
await awaitReconciliation()
}

public func putContext(context: ConfidenceStruct, removedKeys: [String] = []) async {
await withSemaphoreAsync { [weak self] in
guard let self = self else {
return
}
public func putContextAndWait(context: ConfidenceStruct, removedKeys: [String] = []) async {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
let newContext = contextManager.updateContext(withValues: context, removedKeys: removedKeys)
do {
try await self.fetchAndActivate()
Expand All @@ -181,13 +179,12 @@ public class Confidence: ConfidenceEventSender {
debugLogger?.logMessage(message: "Error when putting context: \(error)", isWarning: true)
}
}
await awaitReconciliation()
}

public func putContext(context: ConfidenceStruct) async {
await withSemaphoreAsync { [weak self] in
guard let self = self else {
return
}
public func putContextAndWait(context: ConfidenceStruct) async {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
let newContext = contextManager.updateContext(withValues: context, removedKeys: [])
do {
try await fetchAndActivate()
Expand All @@ -200,6 +197,25 @@ public class Confidence: ConfidenceEventSender {
isWarning: true)
}
}
await awaitReconciliation()
}

public func removeContextAndWait(key: String) async {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
let newContext = contextManager.updateContext(withValues: [:], removedKeys: [key])
do {
try await self.fetchAndActivate()
debugLogger?.logContext(
action: "RemoveContext",
context: newContext)
} catch {
debugLogger?.logMessage(
message: "Error when removing context key: \(error)",
isWarning: true)
}
}
await awaitReconciliation()
}

/**
Expand All @@ -212,25 +228,60 @@ public class Confidence: ConfidenceEventSender {
context: newContext)
}

public func removeContext(key: String) async {
await withSemaphoreAsync { [weak self] in
guard let self = self else {
return
}
let newContext = contextManager.updateContext(withValues: [:], removedKeys: [key])
public func putContext(key: String, value: ConfidenceValue) {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
await putContextAndWait(key: key, value: value)
}
}

public func putContext(context: ConfidenceStruct) {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
await putContextAndWait(context: context)
}
}

public func putContext(context: ConfidenceStruct, removeKeys removedKeys: [String] = []) {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
await putContextAndWait(context: context, removedKeys: removedKeys)
}
}

public func removeContext(key: String) {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
await removeContextAndWait(key: key)
}
}

public func putContext(context: ConfidenceStruct, removedKeys: [String]) {
self.currentFetchTask?.cancel()
self.currentFetchTask = Task {
let newContext = contextManager.updateContext(withValues: context, removedKeys: removedKeys)
do {
try await self.fetchAndActivate()
debugLogger?.logContext(
action: "RemoveContext",
context: newContext)
} catch {
debugLogger?.logMessage(
message: "Error when removing context key: \(error)",
message: "Error when putting context: \(error)",
isWarning: true)
}
}
}

/**
Ensures all the already-started context changes prior to this function have been reconciliated
*/
public func awaitReconciliation() async {
if let task = self.currentFetchTask {
await task.value
}
}

public func withContext(_ context: ConfidenceStruct) -> ConfidenceEventSender {
return Self.init(
clientSecret: clientSecret,
Expand Down Expand Up @@ -270,7 +321,7 @@ public class Confidence: ConfidenceEventSender {
.sink { [weak self] context in
Task { [weak self] in
guard let self = self else { return }
await self.putContext(context: context)
await self.putContextAndWait(context: context)
}
}
.store(in: &cancellables)
Expand All @@ -288,17 +339,6 @@ public class Confidence: ConfidenceEventSender {
public func flush() {
eventSenderEngine.flush()
}

private func withSemaphoreAsync(callback: @escaping () async -> Void) async {
await withCheckedContinuation { continuation in
DispatchQueue.global().async {
self.semaphore.wait()
continuation.resume()
}
}
await callback()
semaphore.signal()
}
}

private class ContextManager {
Expand Down
28 changes: 24 additions & 4 deletions Sources/Confidence/ConfidenceEventSender.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,42 @@ public protocol ConfidenceEventSender: ConfidenceContextProvider {
Adds/override entry to local context data
Triggers fetchAndActivate after the context change
*/
func putContext(key: String, value: ConfidenceValue) async
func putContextAndWait(key: String, value: ConfidenceValue) async
/**
Adds/override entry to local context data
Triggers fetchAndActivate after the context change
*/
func putContext(context: ConfidenceStruct) async
func putContextAndWait(context: ConfidenceStruct) async
/**
Removes entry from localcontext data
It hides entries with this key from parents' data (without modifying parents' data)
Triggers fetchAndActivate after the context change
*/
func removeContext(key: String) async
func removeContextAndWait(key: String) async
/**
Combination of putContext and removeContext
*/
func putContext(context: ConfidenceStruct, removedKeys: [String]) async
func putContextAndWait(context: ConfidenceStruct, removedKeys: [String]) async
/**
Adds/override entry to local context data
Triggers fetchAndActivate after the context change
*/
func putContext(key: String, value: ConfidenceValue)
/**
Adds/override entry to local context data
Triggers fetchAndActivate after the context change
*/
func putContext(context: ConfidenceStruct)
/**
Removes entry from localcontext data
It hides entries with this key from parents' data (without modifying parents' data)
Triggers fetchAndActivate after the context change
*/
func removeContext(key: String)
/**
Combination of putContext and removeContext
*/
func putContext(context: ConfidenceStruct, removedKeys: [String])
/**
Creates a child event sender instance that maintains access to its parent's data
*/
Expand Down
2 changes: 1 addition & 1 deletion Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public class ConfidenceFeatureProvider: FeatureProvider {
} ?? []

Task {
await confidence.putContext(context: ConfidenceTypeMapper.from(ctx: newContext), removedKeys: removedKeys)
confidence.putContext(context: ConfidenceTypeMapper.from(ctx: newContext), removedKeys: removedKeys)
}
}

Expand Down
Loading

0 comments on commit 1a0b34a

Please sign in to comment.