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

fix: Fix optional parameters in callbacks #143

Merged
merged 4 commits into from
Sep 23, 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
2 changes: 1 addition & 1 deletion example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1891,4 +1891,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 83ff0c12eb91f4379210b71045e485dacb487fe9

COCOAPODS: 1.14.3
COCOAPODS: 1.15.2
22 changes: 22 additions & 0 deletions example/src/getTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,28 @@ export function getTests(
.didNotThrow()
.equals(true)
),
createTest('callWithOptional(undefined)', async () =>
it(() => {
let calledBack: number | undefined
testObject.callWithOptional(undefined, (val) => {
calledBack = val
})
return calledBack
})
.didNotThrow()
.equals(undefined)
),
createTest('callWithOptional(433)', async () =>
it(() => {
let calledBack: number | undefined
testObject.callWithOptional(433, (val) => {
calledBack = val
})
return calledBack
})
.didNotThrow()
.equals(433)
),
...('getValueFromJSCallback' in testObject
? [
createTest('getValueFromJSCallback(...)', async () =>
Expand Down
3 changes: 2 additions & 1 deletion packages/nitrogen/src/syntax/createType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ export function createType(type: TSMorphType, isOptional: boolean): Type {
const parameters = callSignature.getParameters().map((p) => {
const declaration = p.getValueDeclarationOrThrow()
const parameterType = p.getTypeAtLocation(declaration)
return createNamedType(p.getName(), parameterType, p.isOptional())
const isNullable = parameterType.isNullable() || p.isOptional()
return createNamedType(p.getName(), parameterType, isNullable)
})
return new FunctionType(returnType, parameters)
} else if (isPromise(type)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ class HybridTestObjectKotlin: HybridTestObjectSwiftKotlinSpec() {
callback()
}

override fun callWithOptional(value: Double?, callback: (maybe: Double?) -> Unit): Unit {
callback(value)
}

override fun callAll(first: () -> Unit, second: () -> Unit, third: () -> Unit) {
first()
second()
Expand Down
5 changes: 5 additions & 0 deletions packages/react-native-nitro-image/cpp/HybridTestObjectCpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ void HybridTestObjectCpp::callCallback(const std::function<void()>& callback) {
callback();
}

void HybridTestObjectCpp::callWithOptional(std::optional<double> value,
const std::function<void(std::optional<double> /* maybe */)>& callback) {
callback(value);
}

void HybridTestObjectCpp::getValueFromJSCallback(const std::function<std::future<double>()>& getValue) {
ThreadPool::getSharedPool()->run([=]() {
std::future<double> future = getValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class HybridTestObjectCpp : public HybridTestObjectCppSpec {
std::future<int64_t> calculateFibonacciAsync(double value) override;
std::future<void> wait(double seconds) override;
void callCallback(const std::function<void()>& callback) override;
void callWithOptional(std::optional<double> value, const std::function<void(std::optional<double> /* maybe */)>& callback) override;
void getValueFromJSCallback(const std::function<std::future<double>()>& getValue) override;
std::future<double> getValueFromJSCallbackAndWait(const std::function<std::future<double>()>& getValue) override;
void callAll(const std::function<void()>& first, const std::function<void()>& second, const std::function<void()>& third) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ class HybridTestObjectSwift : HybridTestObjectSwiftKotlinSpec {
callback()
}

func callWithOptional(value: Double?, callback: @escaping ((_ maybe: Double?) -> Void)) throws -> Void {
callback(value)
}

var hybridContext: margelo.nitro.HybridContext = .init()

var memorySize: Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "JHybridImageFactorySpec.hpp"
#include "JHybridTestObjectSwiftKotlinSpec.hpp"
#include "JFunc_void.hpp"
#include "JFunc_void_std__optional_double_.hpp"
#include <NitroModules/JNISharedPtr.hpp>
#include "HybridTestObjectCpp.hpp"

Expand All @@ -36,6 +37,7 @@ int initialize(JavaVM* vm) {
margelo::nitro::image::JFunc_void::registerNatives();
margelo::nitro::image::JFunc_void::registerNatives();
margelo::nitro::image::JFunc_void::registerNatives();
margelo::nitro::image::JFunc_void_std__optional_double_::registerNatives();

// Register Nitro Hybrid Objects
HybridObjectRegistry::registerHybridObjectConstructor(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
///
/// JFunc_void_std__optional_double_.hpp
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
/// https://github.com/mrousavy/nitro
/// Copyright © 2024 Marc Rousavy @ Margelo
///

#pragma once

#include <fbjni/fbjni.h>
#include <functional>

#include <functional>
#include <optional>

namespace margelo::nitro::image {

using namespace facebook;

/**
* C++ representation of the callback Func_void_std__optional_double_.
* This is a Kotlin `(maybe: Double?) -> Unit`, backed by a `std::function<...>`.
*/
struct JFunc_void_std__optional_double_ final: public jni::HybridClass<JFunc_void_std__optional_double_> {
public:
static jni::local_ref<JFunc_void_std__optional_double_::javaobject> fromCpp(const std::function<void(std::optional<double> /* maybe */)>& func) {
return JFunc_void_std__optional_double_::newObjectCxxArgs(func);
}

public:
void call(const jni::alias_ref<jni::JDouble>& maybe) {
return _func(maybe != nullptr ? std::make_optional(maybe->value()) : std::nullopt);
}

public:
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/image/Func_void_std__optional_double_;";
static void registerNatives() {
registerHybrid({makeNativeMethod("call", JFunc_void_std__optional_double_::call)});
}

private:
explicit JFunc_void_std__optional_double_(const std::function<void(std::optional<double> /* maybe */)>& func): _func(func) { }

private:
friend HybridBase;
std::function<void(std::optional<double> /* maybe */)> _func;
};

} // namespace margelo::nitro::image
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ namespace NitroModules { class ArrayBuffer; }
#include <NitroModules/JArrayBuffer.hpp>
#include <functional>
#include "JFunc_void.hpp"
#include "JFunc_void_std__optional_double_.hpp"

namespace margelo::nitro::image {

Expand Down Expand Up @@ -221,6 +222,10 @@ namespace margelo::nitro::image {
static const auto method = _javaPart->getClass()->getMethod<void(jni::alias_ref<JFunc_void::javaobject> /* first */, jni::alias_ref<JFunc_void::javaobject> /* second */, jni::alias_ref<JFunc_void::javaobject> /* third */)>("callAll");
method(_javaPart, JFunc_void::fromCpp(first), JFunc_void::fromCpp(second), JFunc_void::fromCpp(third));
}
void JHybridTestObjectSwiftKotlinSpec::callWithOptional(std::optional<double> value, const std::function<void(std::optional<double> /* maybe */)>& callback) {
static const auto method = _javaPart->getClass()->getMethod<void(jni::alias_ref<jni::JDouble> /* value */, jni::alias_ref<JFunc_void_std__optional_double_::javaobject> /* callback */)>("callWithOptional");
method(_javaPart, value.has_value() ? jni::JDouble::valueOf(value.value()) : nullptr, JFunc_void_std__optional_double_::fromCpp(callback));
}
Car JHybridTestObjectSwiftKotlinSpec::getCar() {
static const auto method = _javaPart->getClass()->getMethod<jni::local_ref<JCar>()>("getCar");
auto result = method(_javaPart);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ namespace margelo::nitro::image {
std::future<void> wait(double seconds) override;
void callCallback(const std::function<void()>& callback) override;
void callAll(const std::function<void()>& first, const std::function<void()>& second, const std::function<void()>& third) override;
void callWithOptional(std::optional<double> value, const std::function<void(std::optional<double> /* maybe */)>& callback) override;
Car getCar() override;
bool isCarElectric(const Car& car) override;
std::optional<Person> getDriver(const Car& car) override;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
///
/// Func_void_std__optional_double_.kt
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
/// https://github.com/mrousavy/nitro
/// Copyright © 2024 Marc Rousavy @ Margelo
///

package com.margelo.nitro.image

import androidx.annotation.Keep
import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip
import dalvik.annotation.optimization.FastNative

/**
* Represents the JavaScript callback `(maybe: optional) => void`.
* This is implemented in C++, via a `std::function<...>`.
*/
@DoNotStrip
@Keep
@Suppress("RedundantSuppression", "ConvertSecondaryConstructorToPrimary", "RedundantUnitReturnType", "KotlinJniMissingFunction", "ClassName", "unused")
class Func_void_std__optional_double_ {
@DoNotStrip
@Keep
private val mHybridData: HybridData

@DoNotStrip
@Keep
private constructor(hybridData: HybridData) {
mHybridData = hybridData
}

/**
* Converts this function to a Kotlin Lambda.
* This exists purely as syntactic sugar, and has minimal runtime overhead.
*/
fun toLambda(): (maybe: Double?) -> Unit = this::call

/**
* Call the given JS callback.
* @throws Throwable if the JS function itself throws an error, or if the JS function/runtime has already been deleted.
*/
@FastNative
external fun call(maybe: Double?): Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ abstract class HybridTestObjectSwiftKotlinSpec: HybridObject() {
return result
}

@DoNotStrip
@Keep
abstract fun callWithOptional(value: Double?, callback: (maybe: Double?) -> Unit): Unit

@DoNotStrip
@Keep
private fun callWithOptional(value: Double?, callback: Func_void_std__optional_double_): Unit {
val result = callWithOptional(value, callback.toLambda())
return result
}

@DoNotStrip
@Keep
abstract fun getCar(): Car
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,42 @@ namespace margelo::nitro::image::bridge::swift {
return PromiseHolder<double>();
}

/**
* Specialized version of `std::optional<double>`.
*/
using std__optional_double_ = std::optional<double>;
inline std::optional<double> create_std__optional_double_(const double& value) {
return std::optional<double>(value);
}

/**
* Specialized version of `std::function<void(std::optional<double>)>`.
*/
using Func_void_std__optional_double_ = std::function<void(std::optional<double> /* maybe */)>;
/**
* Wrapper class for a `std::function<void(std::optional<double> / * maybe * /)>`, this can be used from Swift.
*/
class Func_void_std__optional_double__Wrapper {
public:
explicit Func_void_std__optional_double__Wrapper(const std::function<void(std::optional<double> /* maybe */)>& func): function(func) {}
explicit Func_void_std__optional_double__Wrapper(std::function<void(std::optional<double> /* maybe */)>&& func): function(std::move(func)) {}

void call(std::optional<double> maybe) const {
function(maybe);
}

std::function<void(std::optional<double> /* maybe */)> function;
};
inline Func_void_std__optional_double_ create_Func_void_std__optional_double_(void* closureHolder, void(*call)(void* /* closureHolder */, std::optional<double>), void(*destroy)(void*)) {
std::shared_ptr<void> sharedClosureHolder(closureHolder, destroy);
return Func_void_std__optional_double_([sharedClosureHolder, call](std::optional<double> maybe) -> void {
call(sharedClosureHolder.get(), maybe);
});
}
inline std::shared_ptr<Func_void_std__optional_double__Wrapper> share_Func_void_std__optional_double_(const Func_void_std__optional_double_& value) {
return std::make_shared<Func_void_std__optional_double__Wrapper>(value);
}

/**
* Specialized version of `std::function<std::future<std::string>()>`.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ namespace margelo::nitro::image {
inline void callAll(const std::function<void()>& first, const std::function<void()>& second, const std::function<void()>& third) override {
_swiftPart.callAll(first, second, third);
}
inline void callWithOptional(std::optional<double> value, const std::function<void(std::optional<double> /* maybe */)>& callback) override {
_swiftPart.callWithOptional(value, callback);
}
inline Car getCar() override {
auto __result = _swiftPart.getCar();
return __result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public protocol HybridTestObjectSwiftKotlinSpec: HybridObjectSpec {
func wait(seconds: Double) throws -> Promise<Void>
func callCallback(callback: @escaping (() -> Void)) throws -> Void
func callAll(first: @escaping (() -> Void), second: @escaping (() -> Void), third: @escaping (() -> Void)) throws -> Void
func callWithOptional(value: Double?, callback: @escaping ((_ maybe: Double?) -> Void)) throws -> Void
func getCar() throws -> Car
func isCarElectric(car: Car) throws -> Bool
func getDriver(car: Car) throws -> Person?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,28 @@ public final class HybridTestObjectSwiftKotlinSpecCxx {
}
}

@inline(__always)
public func callWithOptional(value: bridge.std__optional_double_, callback: bridge.Func_void_std__optional_double_) -> Void {
do {
try self.implementation.callWithOptional(value: value.value, callback: { () -> ((Double?) -> Void) in
let shared = bridge.share_Func_void_std__optional_double_(callback)
return { (maybe: Double?) -> Void in
shared.pointee.call({ () -> bridge.std__optional_double_ in
if let actualValue = maybe {
return bridge.create_std__optional_double_(actualValue)
} else {
return .init()
}
}())
}
}())
return
} catch {
let message = "\(error.localizedDescription)"
fatalError("Swift errors can currently not be propagated to C++! See https://github.com/swiftlang/swift/issues/75290 (Error: \(message))")
}
}

@inline(__always)
public func getCar() -> Car {
do {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ namespace margelo::nitro::image {
prototype.registerHybridMethod("wait", &HybridTestObjectCppSpec::wait);
prototype.registerHybridMethod("callCallback", &HybridTestObjectCppSpec::callCallback);
prototype.registerHybridMethod("getValueFromJSCallback", &HybridTestObjectCppSpec::getValueFromJSCallback);
prototype.registerHybridMethod("callWithOptional", &HybridTestObjectCppSpec::callWithOptional);
prototype.registerHybridMethod("getValueFromJSCallbackAndWait", &HybridTestObjectCppSpec::getValueFromJSCallbackAndWait);
prototype.registerHybridMethod("callAll", &HybridTestObjectCppSpec::callAll);
prototype.registerHybridMethod("getValueFromJsCallback", &HybridTestObjectCppSpec::getValueFromJsCallback);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ namespace margelo::nitro::image {
virtual std::future<void> wait(double seconds) = 0;
virtual void callCallback(const std::function<void()>& callback) = 0;
virtual void getValueFromJSCallback(const std::function<std::future<double>()>& getValue) = 0;
virtual void callWithOptional(std::optional<double> value, const std::function<void(std::optional<double> /* maybe */)>& callback) = 0;
virtual std::future<double> getValueFromJSCallbackAndWait(const std::function<std::future<double>()>& getValue) = 0;
virtual void callAll(const std::function<void()>& first, const std::function<void()>& second, const std::function<void()>& third) = 0;
virtual std::future<void> getValueFromJsCallback(const std::function<std::future<std::string>()>& callback, const std::function<void(const std::string& /* valueFromJs */)>& andThenCall) = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ namespace margelo::nitro::image {
prototype.registerHybridMethod("wait", &HybridTestObjectSwiftKotlinSpec::wait);
prototype.registerHybridMethod("callCallback", &HybridTestObjectSwiftKotlinSpec::callCallback);
prototype.registerHybridMethod("callAll", &HybridTestObjectSwiftKotlinSpec::callAll);
prototype.registerHybridMethod("callWithOptional", &HybridTestObjectSwiftKotlinSpec::callWithOptional);
prototype.registerHybridMethod("getCar", &HybridTestObjectSwiftKotlinSpec::getCar);
prototype.registerHybridMethod("isCarElectric", &HybridTestObjectSwiftKotlinSpec::isCarElectric);
prototype.registerHybridMethod("getDriver", &HybridTestObjectSwiftKotlinSpec::getDriver);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ namespace margelo::nitro::image {
virtual std::future<void> wait(double seconds) = 0;
virtual void callCallback(const std::function<void()>& callback) = 0;
virtual void callAll(const std::function<void()>& first, const std::function<void()>& second, const std::function<void()>& third) = 0;
virtual void callWithOptional(std::optional<double> value, const std::function<void(std::optional<double> /* maybe */)>& callback) = 0;
virtual Car getCar() = 0;
virtual bool isCarElectric(const Car& car) = 0;
virtual std::optional<Person> getDriver(const Car& car) = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export interface TestObjectCpp extends HybridObject<{ ios: 'c++' }> {
// Callbacks
callCallback(callback: () => void): void
getValueFromJSCallback(getValue: () => number): void
callWithOptional(
value: number | undefined,
callback: (maybe: number | undefined) => void
): void
getValueFromJSCallbackAndWait(getValue: () => number): Promise<number>
callAll(first: () => void, second: () => void, third: () => void): void
getValueFromJsCallback(
Expand Down Expand Up @@ -158,6 +162,10 @@ export interface TestObjectSwiftKotlin
// Callbacks
callCallback(callback: () => void): void
callAll(first: () => void, second: () => void, third: () => void): void
callWithOptional(
value: number | undefined,
callback: (maybe: number | undefined) => void
): void
// TODO: Callbacks that return a value are not supported in Swift yet!
// getValueFromJSCallback(getValue: () => number): void
// getValueFromJSCallbackAndWait(getValue: () => number): Promise<number>
Expand Down
Loading