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 1 commit
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
11 changes: 11 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,15 @@
else { return nil }
return BaseFlowState.createInstance(value: jsState)
}

var constantsController: ConstantsController? {
guard
let constantControllerJSValue = jsPlayerReference?.objectForKeyedSubscript("constantsController")
else {
return nil

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

View check run for this annotation

Codecov / codecov/patch

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

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

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

public class ConstantsController {
var constantsController: JSValue?

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?.toString() as? T
cehan-Chloe marked this conversation as resolved.
Show resolved Hide resolved
} else {
let value = self.constantsController?.invokeMethod("getConstants", withArguments: [key, namespace])
return value?.toString() as? T
}
}

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])
}

public func setTemporaryValues(data: Any, namespace: String) -> Void {
self.constantsController?.invokeMethod("setTemporaryValues", withArguments: [data, namespace])
}

public func clearTemporaryValues() -> Void {
self.constantsController?.invokeMethod("clearTemporaryValues", withArguments: [])
}

public init(constantsController: JSValue) {
self.constantsController = constantsController
}
}
95 changes: 95 additions & 0 deletions ios/core/Tests/HeadlessPlayerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,101 @@ 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"
]
]

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")
XCTAssertEqual(middleName, "undefined")

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 nonExistantNamespace: String? = constantsController.getConstants(key:"test", namespace: "foo")
XCTAssertEqual(nonExistantNamespace, "undefined")
cehan-Chloe marked this conversation as resolved.
Show resolved Hide resolved

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
12 changes: 12 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,23 @@ 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 }
}

var constantsController: ConstantsController? {
get { self[ConstantsControllerStateKey.self] }
set { self[ConstantsControllerStateKey.self] = newValue }
}
}

internal extension SwiftUIPlayer {
Expand Down