Skip to content

Commit

Permalink
ios expose constantsController #446
Browse files Browse the repository at this point in the history
  • Loading branch information
cehan-Chloe committed Aug 9, 2024
1 parent 0c12e2a commit 570ff7c
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
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 @@ public protocol HeadlessPlayer {
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 @@ public extension HeadlessPlayer {
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
} else {
let value = self.constantsController?.invokeMethod("getConstants", withArguments: [key, namespace])
return value?.toString() as? T
}
}

public func addConstants(data: Any, namespace: String) -> Void {
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")

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

0 comments on commit 570ff7c

Please sign in to comment.