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

ios expose constantsController #446 #487

Merged
merged 4 commits into from
Aug 14, 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
12 changes: 12 additions & 0 deletions ios/core/Sources/Player/HeadlessPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@
var hooks: HooksType? { get }
/// A logger reference for use in plugins to log through the shared player logger
var logger: TapableLogger { get }
/// A reference to the Key/Value store for constants and context for player
var constantsController: ConstantsController? { get }

/**
Sets up the core javascript player in the given context
Expand Down Expand Up @@ -169,6 +171,16 @@
else { return nil }
return BaseFlowState.createInstance(value: jsState)
}

/// The constants and context for player
var constantsController: ConstantsController? {
guard
let constantControllerJSValue = jsPlayerReference?.objectForKeyedSubscript("constantsController")
else {
return nil

Check warning on line 180 in ios/core/Sources/Player/HeadlessPlayer.swift

View check run for this annotation

Codecov / codecov/patch

ios/core/Sources/Player/HeadlessPlayer.swift#L180

Added line #L180 was not covered by tests
}
return ConstantsController(constantsController: constantControllerJSValue)
}

/**
Sets up the core javascript player in the given context
Expand Down
46 changes: 46 additions & 0 deletions ios/core/Sources/Types/Core/ConstantsController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import JavaScriptCore

public class ConstantsController {
var constantsController: JSValue?

/// Function to retrieve constants from the providers store
/// - Parameters:
/// - key: Key used for the store access
/// - namespace: Namespace values were loaded under
/// - fallback:Optional - if key doesn't exist in namespace what to return (will return unknown if not provided)
/// - Returns: Constant values from store
public func getConstants<T>(key: Any, namespace: String, fallback: Any? = nil) -> T? {
if let fallbackValue = fallback {
let value = self.constantsController?.invokeMethod("getConstants", withArguments: [key, namespace, fallbackValue])
return value?.toObject() as? T
} else {
let value = self.constantsController?.invokeMethod("getConstants", withArguments: [key, namespace])
return value?.toObject() as? T
}
}

/// Function to add constants to the providers store
/// - Parameters:
/// - data: Values to add to the constants store
/// - namespace: Namespace values to be added under
public func addConstants(data: Any, namespace: String) -> Void {
cehan-Chloe marked this conversation as resolved.
Show resolved Hide resolved
self.constantsController?.invokeMethod("addConstants", withArguments: [data, namespace])
}

/// Function to set values to temporarily override certain keys in the perminant store
/// - Parameters:
/// - data: Values to override store with
/// - namespace: Namespace to override
public func setTemporaryValues(data: Any, namespace: String) -> Void {
self.constantsController?.invokeMethod("setTemporaryValues", withArguments: [data, namespace])
}

/// Clears any temporary values that were previously set
public func clearTemporaryValues() -> Void {
self.constantsController?.invokeMethod("clearTemporaryValues", withArguments: [])
}

public init(constantsController: JSValue) {
self.constantsController = constantsController
}
}
99 changes: 99 additions & 0 deletions ios/core/Tests/HeadlessPlayerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,105 @@ class HeadlessPlayerTests: XCTestCase {
player.start(flow: FlowData.COUNTER) { _ in}
wait(for: [updateExp], timeout: 1)
}

func testConstantsController() {
let player = HeadlessPlayerImpl(plugins: [])

guard let constantsController = player.constantsController else { return }

// Basic get/set tests
let data: Any = [
"firstname": "john",
"lastname": "doe",
"favorite": [
"color": "red"
],
"age": 1
]

constantsController.addConstants(data: data, namespace: "constants")

let firstname: String? = constantsController.getConstants(key: "firstname", namespace: "constants")
XCTAssertEqual(firstname, "john")

let middleName: String? = constantsController.getConstants(key:"middlename", namespace: "constants")
XCTAssertNil(middleName)

let middleNameSafe: String? = constantsController.getConstants(key:"middlename", namespace: "constants", fallback: "A")
XCTAssertEqual(middleNameSafe, "A")

let favoriteColor: String? = constantsController.getConstants(key:"favorite.color", namespace: "constants")
XCTAssertEqual(favoriteColor, "red")

let age: Int? = constantsController.getConstants(key:"age", namespace: "constants")
XCTAssertEqual(age, 1)

let nonExistantNamespace: String? = constantsController.getConstants(key:"test", namespace: "foo")
XCTAssertNil(nonExistantNamespace)

let nonExistantNamespaceWithFallback: String? = constantsController.getConstants(key:"test", namespace: "foo", fallback: "B")
XCTAssertEqual(nonExistantNamespaceWithFallback, "B")

// Test and make sure keys override properly
let newData: Any = [
"favorite": [
"color": "blue",
],
];

constantsController.addConstants(data: newData, namespace: "constants");

let newFavoriteColor: String? = constantsController.getConstants(key: "favorite.color", namespace:"constants")
XCTAssertEqual(newFavoriteColor, "blue")
}

func testConstantsControllerTempValues() {
let player = HeadlessPlayerImpl(plugins: [])

guard let constantsController = player.constantsController else { return }

// Add initial constants
let data: Any = [
"firstname": "john",
"lastname": "doe",
"favorite": [
"color": "red"
]
]
constantsController.addConstants(data: data, namespace: "constants")

// Override with temporary values
let tempData: Any = [
"firstname": "jane",
"favorite": [
"color": "blue"
]
]
constantsController.setTemporaryValues(data:tempData, namespace: "constants")

// Test temporary override
let firstnameTemp: String? = constantsController.getConstants(key:"firstname", namespace: "constants")
XCTAssertEqual(firstnameTemp, "jane")

let favoriteColorTemp: String? = constantsController.getConstants(key: "favorite.color", namespace: "constants")
XCTAssertEqual(favoriteColorTemp, "blue")

// Test fallback to original values when temporary values are not present
let lastnameTemp: String? = constantsController.getConstants(key: "lastname", namespace: "constants")
XCTAssertEqual(lastnameTemp, "doe")

// Reset temp and values should be the same as the original data
constantsController.clearTemporaryValues();

let firstname: String? = constantsController.getConstants(key:"firstname", namespace: "constants")
XCTAssertEqual(firstname, "john")

let favoriteColor: String? = constantsController.getConstants(key: "favorite.color", namespace: "constants")
XCTAssertEqual(favoriteColor, "red")

let lastname: String? = constantsController.getConstants(key: "lastname", namespace: "constants")
XCTAssertEqual(lastname, "doe")
}
}

class FakePlugin: JSBasePlugin, NativePlugin {
Expand Down
13 changes: 13 additions & 0 deletions ios/swiftui/Sources/SwiftUIPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ public struct SwiftUIPlayer: View, HeadlessPlayer {
public var body: some View {
bodyContent
.environment(\.inProgressState, (state as? InProgressState))
.environment(\.constantsController, constantsController)
// forward results from our Context along to our result binding
.onReceive(context.$result.debounce(for: 0.1, scheduler: RunLoop.main)) {
self.result = $0
Expand Down Expand Up @@ -232,12 +233,24 @@ struct InProgressStateKey: EnvironmentKey {
static var defaultValue: InProgressState?
}

/// EnvironmentKey for storing `constantsController`
struct ConstantsControllerStateKey: EnvironmentKey {
/// The default value for `@Environment(\.constantsController)`
static var defaultValue: ConstantsController? = nil
}

public extension EnvironmentValues {
/// The `InProgressState` of Player if it is in progress, and in scope
var inProgressState: InProgressState? {
get { self[InProgressStateKey.self] }
set { self[InProgressStateKey.self] = newValue }
}

/// The ConstantsController reference of Player
var constantsController: ConstantsController? {
get { self[ConstantsControllerStateKey.self] }
set { self[ConstantsControllerStateKey.self] = newValue }
}
}

internal extension SwiftUIPlayer {
Expand Down