Skip to content
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
75 changes: 52 additions & 23 deletions cpp/TransformerTextInputRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ namespace rntti {

namespace {
std::weak_ptr<worklets::WorkletRuntime> gUiRuntime;

void LogTransformerError(jsi::Runtime &runtime, const std::string &message) {
try {
auto console = runtime.global().getPropertyAsObject(runtime, "console");
auto errorFunction = console.getPropertyAsFunction(runtime, "error");
errorFunction.call(
runtime,
jsi::String::createFromUtf8(
runtime, "[rntti] Transformer threw an error: " + message));
} catch (const jsi::JSIException &) {
// console is not guaranteed to exist on the worklet runtime.
}
}
} // namespace

void SetUIWorkletRuntime(
Expand Down Expand Up @@ -59,31 +72,47 @@ std::optional<TransformResult> RunTransformer(

auto transformerFunction =
transformerValue.asObject(jsiRuntime).asFunction(jsiRuntime);
auto resultValue = uiRuntime->runSync(
transformerFunction,
jsi::String::createFromUtf8(jsiRuntime, value),
jsi::Value(selection.start),
jsi::Value(selection.end),
jsi::Value(transform));

TransformResult result;
auto resultObject = resultValue.asObject(jsiRuntime);
auto valueProp = resultObject.getProperty(jsiRuntime, "value");
if (valueProp.isString()) {
result.value = valueProp.asString(jsiRuntime).utf8(jsiRuntime);
}

auto selectionProp = resultObject.getProperty(jsiRuntime, "selection");
if (selectionProp.isObject()) {
auto selectionObject = selectionProp.asObject(jsiRuntime);
auto startProp = selectionObject.getProperty(jsiRuntime, "start");
auto endProp = selectionObject.getProperty(jsiRuntime, "end");
result.selection = SelectionRange{
static_cast<int>(startProp.asNumber()),
static_cast<int>(endProp.asNumber())};
}
try {
auto resultValue = uiRuntime->runSync(
transformerFunction,
jsi::String::createFromUtf8(jsiRuntime, value),
jsi::Value(selection.start),
jsi::Value(selection.end),
jsi::Value(transform));

// In debug builds runSync guards the call: a throwing transformer is
// reported to LogBox and undefined is returned instead of a result
// object.
if (!resultValue.isObject()) {
return std::nullopt;
}

return result;
TransformResult result;
auto resultObject = resultValue.asObject(jsiRuntime);
auto valueProp = resultObject.getProperty(jsiRuntime, "value");
if (valueProp.isString()) {
result.value = valueProp.asString(jsiRuntime).utf8(jsiRuntime);
}

auto selectionProp = resultObject.getProperty(jsiRuntime, "selection");
if (selectionProp.isObject()) {
auto selectionObject = selectionProp.asObject(jsiRuntime);
auto startProp = selectionObject.getProperty(jsiRuntime, "start");
auto endProp = selectionObject.getProperty(jsiRuntime, "end");
result.selection = SelectionRange{
static_cast<int>(startProp.asNumber()),
static_cast<int>(endProp.asNumber())};
}

return result;
} catch (const jsi::JSError &error) {
LogTransformerError(jsiRuntime, error.getMessage());
return std::nullopt;
} catch (const jsi::JSIException &error) {
LogTransformerError(jsiRuntime, error.what());
return std::nullopt;
}
}

} // namespace rntti
21 changes: 21 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ const usernameTransformer = new Transformer(({ value }) => {
return { value: result };
});

// Returns an out-of-bounds selection, which makes the library's selection
// validation throw on every change. Typing in this input must log the error
// instead of crashing the app.
const throwingTransformer = new Transformer(({ value }) => {
'worklet';

return {
value,
selection: { start: value.length + 5, end: value.length + 5 },
};
});

const phoneNumberTransformer = new PhoneNumberTransformer();

const creditCardTransformer = new PatternTransformer({
Expand Down Expand Up @@ -241,6 +253,15 @@ function AppContent() {
/>

<CurrencyExampleCard />

<ExampleCard
title="Throwing Transformer"
description="Worklet throws on every change; errors are logged instead of crashing"
transformer={throwingTransformer}
placeholder="Type here, app should not crash"
exampleValue="hello"
inputProps={{ autoCapitalize: 'none' }}
/>
</KeyboardAwareScrollView>
);
}
Expand Down
Loading