diff --git a/.eslintignore b/.eslintignore
index 26ecb1ae7cc7..aa10a3073f4e 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -14,3 +14,4 @@ web/gtm.js
src/libs/SearchParser/searchParser.js
src/libs/SearchParser/autocompleteParser.js
help/_scripts/**
+modules/**
diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro
index 6d6406551cdd..bef985265d7f 100644
--- a/android/app/proguard-rules.pro
+++ b/android/app/proguard-rules.pro
@@ -10,6 +10,7 @@
# Add any project specific keep options here:
-keep class com.expensify.chat.BuildConfig { *; }
-keep class com.facebook.** { *; }
+-keep class com.margelo.nitro.** { *; }
-keep, allowoptimization, allowobfuscation class expo.modules.** { *; }
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 142d919a7a18..a859703ae719 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+
diff --git a/ios/NewExpensify/Info.plist b/ios/NewExpensify/Info.plist
index 381b10533eef..094609e14d6c 100644
--- a/ios/NewExpensify/Info.plist
+++ b/ios/NewExpensify/Info.plist
@@ -50,6 +50,8 @@
ITSAppUsesNonExemptEncryption
+ NSContactsUsageDescription
+ Import contacts from your phone so your favorite people are always a tap away.
LSApplicationQueriesSchemes
venmo
diff --git a/ios/Podfile b/ios/Podfile
index 41dc5179752d..bdad8a0ec396 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -24,6 +24,7 @@ prepare_react_native_project!
setup_permissions([
'Camera',
+ 'Contacts',
'LocationAccuracy',
'LocationAlways',
'LocationWhenInUse'
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 18eba3d79c27..65c7b896b8e3 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -28,6 +28,28 @@ PODS:
- AppAuth/Core
- AppLogs (0.1.0)
- boost (1.84.0)
+ - ContactsModule (0.0.1):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - NitroModules
+ - RCT-Folly (= 2024.01.01.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
- DoubleConversion (1.1.6)
- EXAV (14.0.7):
- ExpoModulesCore
@@ -287,6 +309,29 @@ PODS:
- nanopb/encode (= 2.30908.0)
- nanopb/decode (2.30908.0)
- nanopb/encode (2.30908.0)
+ - NitroModules (0.18.1):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.01.01.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-callinvoker
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-jsi
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
- Onfido (29.7.2)
- onfido-react-native-sdk (10.6.0):
- DoubleConversion
@@ -2729,6 +2774,7 @@ DEPENDENCIES:
- AirshipServiceExtension
- AppLogs (from `../node_modules/react-native-app-logs/AppLogsPod`)
- boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`)
+ - ContactsModule (from `../modules/ContactsNitroModule`)
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- EXAV (from `../node_modules/expo-av/ios`)
- EXImageLoader (from `../node_modules/expo-image-loader/ios`)
@@ -2744,6 +2790,7 @@ DEPENDENCIES:
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- lottie-react-native (from `../node_modules/lottie-react-native`)
+ - NitroModules (from `../node_modules/react-native-nitro-modules`)
- "onfido-react-native-sdk (from `../node_modules/@onfido/react-native-sdk`)"
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
@@ -2894,6 +2941,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-app-logs/AppLogsPod"
boost:
:podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec"
+ ContactsModule:
+ :path: "../modules/ContactsNitroModule"
DoubleConversion:
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
EXAV:
@@ -2925,6 +2974,8 @@ EXTERNAL SOURCES:
:tag: hermes-2024-08-15-RNv0.75.1-4b3bf912cc0f705b51b71ce1a5b8bd79b93a451b
lottie-react-native:
:path: "../node_modules/lottie-react-native"
+ NitroModules:
+ :path: "../node_modules/react-native-nitro-modules"
onfido-react-native-sdk:
:path: "../node_modules/@onfido/react-native-sdk"
RCT-Folly:
@@ -3139,6 +3190,7 @@ SPEC CHECKSUMS:
AppAuth: 501c04eda8a8d11f179dbe8637b7a91bb7e5d2fa
AppLogs: 3bc4e9b141dbf265b9464409caaa40416a9ee0e0
boost: 26992d1adf73c1c7676360643e687aee6dda994b
+ ContactsModule: 21671b28654413dc28795d1afc3b12eaffa28ed1
DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5
EXAV: afa491e598334bbbb92a92a2f4dd33d7149ad37f
EXImageLoader: ab589d67d6c5f2c33572afea9917304418566334
@@ -3178,6 +3230,7 @@ SPEC CHECKSUMS:
MapboxMaps: e76b14f52c54c40b76ddecd04f40448e6f35a864
MapboxMobileEvents: de50b3a4de180dd129c326e09cd12c8adaaa46d6
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
+ NitroModules: ebe2ba2d01dc03c1f82441561fe6062b8c3c4366
Onfido: f3af62ea1c9a419589c133e3e511e5d2c4f3f8af
onfido-react-native-sdk: 4ccfdeb10f9ccb4a5799d2555cdbc2a068a42c0d
Plaid: c32f22ffce5ec67c9e6147eaf6c4d7d5f8086d89
@@ -3274,7 +3327,7 @@ SPEC CHECKSUMS:
RNLiveMarkdown: 8338447b39fcd86596c74b9e0e9509e365a2dd3b
RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81
rnmapbox-maps: 460d6ff97ae49c7d5708c3212c6521697c36a0c4
- RNPermissions: 0b1429b55af59d1d08b75a8be2459f65a8ac3f28
+ RNPermissions: 9e5c26aaa982fe00743281f6f47fbdc050ebc58f
RNReactNativeHapticFeedback: 73756a3477a5a622fa16862a3ab0d0fc5e5edff5
RNReanimated: 03ba2447d5a7789e2843df2ee05108d93b6441d6
RNScreens: de6e57426ba0e6cbc3fb5b4f496e7f08cb2773c2
@@ -3290,6 +3343,6 @@ SPEC CHECKSUMS:
VisionCamera: c95a8ad535f527562be1fb05fb2fd324578e769c
Yoga: a1d7895431387402a674fd0d1c04ec85e87909b8
-PODFILE CHECKSUM: 615266329434ea4a994dccf622008a2197313c88
+PODFILE CHECKSUM: e744fa802b4bee097ff8d1977dd8f79d16b21547
COCOAPODS: 1.15.2
diff --git a/modules/ContactsNitroModule/.gitignore b/modules/ContactsNitroModule/.gitignore
new file mode 100644
index 000000000000..d3b53dfce541
--- /dev/null
+++ b/modules/ContactsNitroModule/.gitignore
@@ -0,0 +1,78 @@
+# OSX
+#
+.DS_Store
+
+# XDE
+.expo/
+
+# VSCode
+.vscode/
+jsconfig.json
+
+# Xcode
+#
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate
+project.xcworkspace
+
+# Android/IJ
+#
+.classpath
+.cxx
+.gradle
+.idea
+.project
+.settings
+local.properties
+android.iml
+
+# Cocoapods
+#
+example/ios/Pods
+
+# Ruby
+example/vendor/
+
+# node.js
+#
+node_modules/
+npm-debug.log
+yarn-debug.log
+yarn-error.log
+
+# BUCK
+buck-out/
+\.buckd/
+android/app/libs
+android/keystores/debug.keystore
+
+# Yarn
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/sdks
+!.yarn/versions
+
+# Expo
+.expo/
+
+# Turborepo
+.turbo/
+
+# generated by bob
+lib/
diff --git a/modules/ContactsNitroModule/.watchmanconfig b/modules/ContactsNitroModule/.watchmanconfig
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/modules/ContactsNitroModule/.watchmanconfig
@@ -0,0 +1 @@
+{}
diff --git a/modules/ContactsNitroModule/ContactsModule.podspec b/modules/ContactsNitroModule/ContactsModule.podspec
new file mode 100644
index 000000000000..5f0b012c7b52
--- /dev/null
+++ b/modules/ContactsNitroModule/ContactsModule.podspec
@@ -0,0 +1,29 @@
+require "json"
+
+package = JSON.parse(File.read(File.join(__dir__, "package.json")))
+
+Pod::Spec.new do |s|
+ s.name = "ContactsModule"
+ s.version = package["version"]
+ s.summary = package["description"]
+ s.homepage = package["homepage"]
+ s.license = package["license"]
+ s.authors = package["author"]
+
+ s.platforms = { :ios => min_ios_version_supported }
+ s.source = { :git => "https://github.com/mrousavy/nitro.git", :tag => "#{s.version}" }
+
+ s.source_files = [
+ # Implementation (Swift)
+ "ios/**/*.{swift}",
+ # Autolinking/Registration (Objective-C++)
+ "ios/**/*.{m,mm}",
+ # Implementation (C++ objects)
+ "cpp/**/*.{hpp,cpp}",
+ ]
+
+ load 'nitrogen/generated/ios/ContactsModule+autolinking.rb'
+ add_nitrogen_files(s)
+
+ install_modules_dependencies(s)
+end
diff --git a/modules/ContactsNitroModule/android/CMakeLists.txt b/modules/ContactsNitroModule/android/CMakeLists.txt
new file mode 100644
index 000000000000..beb0c308df07
--- /dev/null
+++ b/modules/ContactsNitroModule/android/CMakeLists.txt
@@ -0,0 +1,29 @@
+project(ContactsModule)
+cmake_minimum_required(VERSION 3.9.0)
+
+set (PACKAGE_NAME ContactsModule)
+set (CMAKE_VERBOSE_MAKEFILE ON)
+set (CMAKE_CXX_STANDARD 20)
+
+# Define C++ library and add all sources
+add_library(${PACKAGE_NAME} SHARED
+ src/main/cpp/cpp-adapter.cpp
+)
+
+# Add Nitrogen specs :)
+include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/ContactsModule+autolinking.cmake)
+
+# Set up local includes
+include_directories(
+ "src/main/cpp"
+ "../cpp"
+)
+
+find_library(LOG_LIB log)
+
+# Link all libraries together
+target_link_libraries(
+ ${PACKAGE_NAME}
+ ${LOG_LIB}
+ android # <-- Android core
+)
diff --git a/modules/ContactsNitroModule/android/build.gradle b/modules/ContactsNitroModule/android/build.gradle
new file mode 100644
index 000000000000..0b414c88dea3
--- /dev/null
+++ b/modules/ContactsNitroModule/android/build.gradle
@@ -0,0 +1,130 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath "com.android.tools.build:gradle:7.2.1"
+ }
+}
+
+def reactNativeArchitectures() {
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
+}
+
+def isNewArchitectureEnabled() {
+ return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
+}
+
+apply plugin: "com.android.library"
+apply plugin: 'org.jetbrains.kotlin.android'
+apply from: '../nitrogen/generated/android/ContactsModule+autolinking.gradle'
+
+if (isNewArchitectureEnabled()) {
+ apply plugin: "com.facebook.react"
+}
+
+def getExtOrDefault(name) {
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["ContactsModule_" + name]
+}
+
+def getExtOrIntegerDefault(name) {
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["ContactsModule_" + name]).toInteger()
+}
+
+android {
+ namespace "com.margelo.nitro.contacts"
+
+ ndkVersion getExtOrDefault("ndkVersion")
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
+
+ defaultConfig {
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
+
+ externalNativeBuild {
+ cmake {
+ cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all"
+ arguments "-DANDROID_STL=c++_shared"
+ abiFilters (*reactNativeArchitectures())
+ }
+ }
+ }
+
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+
+ packagingOptions {
+ excludes = [
+ "META-INF",
+ "META-INF/**",
+ "**/libc++_shared.so",
+ "**/libfbjni.so",
+ "**/libjsi.so",
+ "**/libfolly_json.so",
+ "**/libfolly_runtime.so",
+ "**/libglog.so",
+ "**/libhermes.so",
+ "**/libhermes-executor-debug.so",
+ "**/libhermes_executor.so",
+ "**/libreactnativejni.so",
+ "**/libturbomodulejsijni.so",
+ "**/libreact_nativemodule_core.so",
+ "**/libjscexecutor.so"
+ ]
+ }
+
+ buildFeatures {
+ buildConfig true
+ prefab true
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+
+ lintOptions {
+ disable "GradleCompatible"
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ sourceSets {
+ main {
+ if (isNewArchitectureEnabled()) {
+ java.srcDirs += [
+ // React Codegen files
+ "${project.buildDir}/generated/source/codegen/java"
+ ]
+ }
+ }
+ }
+}
+
+repositories {
+ mavenCentral()
+ google()
+}
+
+
+dependencies {
+ // For < 0.71, this will be from the local maven repo
+ // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
+ //noinspection GradleDynamicVersion
+ implementation "com.facebook.react:react-native:+"
+
+ // Add a dependency on NitroModules
+ implementation project(":react-native-nitro-modules")
+}
+
diff --git a/modules/ContactsNitroModule/android/gradle.properties b/modules/ContactsNitroModule/android/gradle.properties
new file mode 100644
index 000000000000..59d3858d1bb9
--- /dev/null
+++ b/modules/ContactsNitroModule/android/gradle.properties
@@ -0,0 +1,5 @@
+ContactsModule_kotlinVersion=1.9.24
+ContactsModule_minSdkVersion=23
+ContactsModule_targetSdkVersion=34
+ContactsModule_compileSdkVersion=34
+ContactsModule_ndkVersion=26.1.10909125
diff --git a/modules/ContactsNitroModule/android/src/main/AndroidManifest.xml b/modules/ContactsNitroModule/android/src/main/AndroidManifest.xml
new file mode 100644
index 000000000000..a2f47b6057db
--- /dev/null
+++ b/modules/ContactsNitroModule/android/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/modules/ContactsNitroModule/android/src/main/cpp/cpp-adapter.cpp b/modules/ContactsNitroModule/android/src/main/cpp/cpp-adapter.cpp
new file mode 100644
index 000000000000..7a88410f3e4d
--- /dev/null
+++ b/modules/ContactsNitroModule/android/src/main/cpp/cpp-adapter.cpp
@@ -0,0 +1,6 @@
+#include
+#include "ContactsModuleOnLoad.hpp"
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
+ return margelo::nitro::contacts::initialize(vm);
+}
diff --git a/modules/ContactsNitroModule/android/src/main/java/com/margelo/nitro/contacts/ContactsModulePackage.java b/modules/ContactsNitroModule/android/src/main/java/com/margelo/nitro/contacts/ContactsModulePackage.java
new file mode 100644
index 000000000000..e8c26844ce86
--- /dev/null
+++ b/modules/ContactsNitroModule/android/src/main/java/com/margelo/nitro/contacts/ContactsModulePackage.java
@@ -0,0 +1,34 @@
+package com.margelo.nitro.contacts;
+
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.module.model.ReactModuleInfoProvider;
+import com.facebook.react.TurboReactPackage;
+import com.margelo.nitro.core.HybridObject;
+import com.margelo.nitro.core.HybridObjectRegistry;
+
+import java.util.HashMap;
+import java.util.function.Supplier;
+
+public class ContactsModulePackage extends TurboReactPackage {
+ @Nullable
+ @Override
+ public NativeModule getModule(String name, ReactApplicationContext reactContext) {
+ return null;
+ }
+
+ @Override
+ public ReactModuleInfoProvider getReactModuleInfoProvider() {
+ return () -> {
+ return new HashMap<>();
+ };
+ }
+
+ static {
+ System.loadLibrary("ContactsModule");
+ }
+}
diff --git a/modules/ContactsNitroModule/android/src/main/java/com/margelo/nitro/contacts/HybridContactsModule.kt b/modules/ContactsNitroModule/android/src/main/java/com/margelo/nitro/contacts/HybridContactsModule.kt
new file mode 100644
index 000000000000..00feaa7660c2
--- /dev/null
+++ b/modules/ContactsNitroModule/android/src/main/java/com/margelo/nitro/contacts/HybridContactsModule.kt
@@ -0,0 +1,166 @@
+package com.margelo.nitro.contacts
+
+import android.Manifest
+import android.content.pm.PackageManager
+import android.provider.ContactsContract
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import com.facebook.react.bridge.ReactApplicationContext
+import com.margelo.nitro.NitroModules
+import com.margelo.nitro.core.Promise
+
+class HybridContactsModule : HybridContactsModuleSpec() {
+ @Volatile
+ private var estimatedMemorySize: Long = 0
+
+ override val memorySize: Long
+ get() = estimatedMemorySize
+
+ private val context: ReactApplicationContext? = NitroModules.applicationContext
+
+ private fun requestContactPermission(): Boolean {
+ val currentActivity = context?.currentActivity
+ return if (currentActivity != null) {
+ ActivityCompat.requestPermissions(
+ currentActivity, arrayOf(REQUIRED_PERMISSION), PERMISSION_REQUEST_CODE
+ )
+ true
+ } else {
+ false
+ }
+ }
+
+ private fun hasPhoneContactsPermission(): Boolean {
+ return context?.let {
+ ContextCompat.checkSelfPermission(it, Manifest.permission.READ_CONTACTS)
+ } == PackageManager.PERMISSION_GRANTED
+ }
+
+ override fun getAll(keys: Array): Promise> {
+ return Promise.parallel {
+ val contacts = mutableListOf()
+ if (!hasPhoneContactsPermission()) {
+ requestContactPermission()
+ return@parallel emptyArray()
+ }
+
+ context?.contentResolver?.let { resolver ->
+ val projection = arrayOf(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.Data.CONTACT_ID,
+ ContactsContract.Data.DISPLAY_NAME,
+ ContactsContract.Contacts.PHOTO_URI,
+ ContactsContract.Contacts.PHOTO_THUMBNAIL_URI,
+ ContactsContract.Data.DATA1,
+ ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME
+ )
+
+ val selection = "${ContactsContract.Data.MIMETYPE} IN (?, ?, ?)"
+ val selectionArgs = arrayOf(
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE,
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
+ )
+
+ val sortOrder = "${ContactsContract.Data.CONTACT_ID} ASC"
+
+ resolver.query(
+ ContactsContract.Data.CONTENT_URI,
+ projection,
+ selection,
+ selectionArgs,
+ sortOrder
+ )?.use { cursor ->
+ val mimeTypeIndex = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE)
+ val contactIdIndex = cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID)
+ val photoUriIndex = cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)
+ val thumbnailUriIndex =
+ cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI)
+ val data1Index = cursor.getColumnIndex(ContactsContract.Data.DATA1)
+ val givenNameIndex =
+ cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)
+ val familyNameIndex =
+ cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME)
+ val middleNameIndex =
+ cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME)
+
+ var currentContact: Contact? = null
+ var currentContactId: String? = null
+ val currentPhoneNumbers = mutableListOf()
+ val currentEmailAddresses = mutableListOf()
+
+ while (cursor.moveToNext()) {
+ val contactId = cursor.getString(contactIdIndex)
+ val mimeType = cursor.getString(mimeTypeIndex)
+
+ if (contactId != currentContactId) {
+ currentContact?.let { contact ->
+ contacts.add(
+ contact.copy(
+ phoneNumbers = currentPhoneNumbers.toTypedArray(),
+ emailAddresses = currentEmailAddresses.toTypedArray()
+ )
+ )
+ }
+ currentPhoneNumbers.clear()
+ currentEmailAddresses.clear()
+ currentContact = Contact(
+ firstName = "",
+ lastName = "",
+ middleName = null,
+ phoneNumbers = emptyArray(),
+ emailAddresses = emptyArray(),
+ imageData = cursor.getString(photoUriIndex) ?: "",
+ thumbnailImageData = cursor.getString(thumbnailUriIndex) ?: ""
+ )
+ currentContactId = contactId
+ }
+
+ when (mimeType) {
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE -> {
+ currentContact = currentContact?.copy(
+ firstName = cursor.getString(givenNameIndex) ?: "",
+ lastName = cursor.getString(familyNameIndex) ?: "",
+ middleName = cursor.getString(middleNameIndex)
+ )
+ }
+
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> {
+ cursor.getString(data1Index)?.let { phone ->
+ currentPhoneNumbers.add(StringHolder(phone))
+ }
+ }
+
+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> {
+ cursor.getString(data1Index)?.let { email ->
+ currentEmailAddresses.add(StringHolder(email))
+ }
+ }
+ }
+ }
+
+ // Add the last contact
+ currentContact?.let { contact ->
+ contacts.add(
+ contact.copy(
+ phoneNumbers = currentPhoneNumbers.toTypedArray(),
+ emailAddresses = currentEmailAddresses.toTypedArray()
+ )
+ )
+ }
+ }
+ }
+
+ // Update memory size based on contact count
+ estimatedMemorySize = contacts.size.toLong() * 1024 // Assume ~1KB per contact
+ contacts.toTypedArray()
+ }
+ }
+
+ companion object {
+ const val PERMISSION_REQUEST_CODE = 1
+ const val REQUIRED_PERMISSION = Manifest.permission.READ_CONTACTS
+ }
+}
diff --git a/modules/ContactsNitroModule/babel.config.js b/modules/ContactsNitroModule/babel.config.js
new file mode 100644
index 000000000000..3e0218e68fc3
--- /dev/null
+++ b/modules/ContactsNitroModule/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ presets: ['module:@react-native/babel-preset'],
+}
diff --git a/modules/ContactsNitroModule/ios/HybridContactsModule.swift b/modules/ContactsNitroModule/ios/HybridContactsModule.swift
new file mode 100644
index 000000000000..59cc3ea31702
--- /dev/null
+++ b/modules/ContactsNitroModule/ios/HybridContactsModule.swift
@@ -0,0 +1,72 @@
+import NitroModules
+import Contacts
+import Foundation
+
+final class HybridContactsModule: HybridContactsModuleSpec {
+ public var hybridContext = margelo.nitro.HybridContext()
+ public var memorySize: Int { MemoryLayout.size }
+
+ private let contactStore = CNContactStore()
+ private let imageDirectory: URL
+ private let fieldToKeyDescriptor: [ContactFields: CNKeyDescriptor] = [
+ .firstName: CNContactGivenNameKey as CNKeyDescriptor,
+ .lastName: CNContactFamilyNameKey as CNKeyDescriptor,
+ .phoneNumbers: CNContactPhoneNumbersKey as CNKeyDescriptor,
+ .emailAddresses: CNContactEmailAddressesKey as CNKeyDescriptor,
+ .middleName: CNContactMiddleNameKey as CNKeyDescriptor,
+ .imageData: CNContactImageDataKey as CNKeyDescriptor,
+ .thumbnailImageData: CNContactThumbnailImageDataKey as CNKeyDescriptor,
+ .givenNameKey: CNContactGivenNameKey as CNKeyDescriptor
+ ]
+
+ init() {
+ imageDirectory = FileManager.default.temporaryDirectory.appendingPathComponent("ContactImages")
+ try? FileManager.default.createDirectory(at: imageDirectory, withIntermediateDirectories: true)
+ }
+
+ func getAll(keys: [ContactFields]) throws -> Promise<[Contact]> {
+ Promise.async { [unowned self] in
+ let keysSet = Set(keys)
+ let keysToFetch = keys.compactMap { self.fieldToKeyDescriptor[$0] }
+ guard !keysToFetch.isEmpty else { return [] }
+
+ let request = CNContactFetchRequest(keysToFetch: keysToFetch)
+ var contacts = [Contact]()
+ contacts.reserveCapacity(1000)
+
+ try self.contactStore.enumerateContacts(with: request) { contact, _ in
+ contacts.append(self.processContact(contact, keysSet: keysSet))
+ }
+
+ return contacts
+ }
+ }
+
+ @inline(__always)
+ private func processContact(_ contact: CNContact, keysSet: Set) -> Contact {
+ Contact(
+ firstName: keysSet.contains(.firstName) ? contact.givenName : nil,
+ lastName: keysSet.contains(.lastName) ? contact.familyName : nil,
+ middleName: keysSet.contains(.middleName) ? contact.middleName : nil,
+ phoneNumbers: keysSet.contains(.phoneNumbers) ? contact.phoneNumbers.map { StringHolder(value: $0.value.stringValue) } : nil,
+ emailAddresses: keysSet.contains(.emailAddresses) ? contact.emailAddresses.map { StringHolder(value: $0.value as String) } : nil,
+ imageData: keysSet.contains(.imageData) ? getImagePath(for: contact, isThumbnail: false) : nil,
+ thumbnailImageData: keysSet.contains(.thumbnailImageData) ? getImagePath(for: contact, isThumbnail: true) : nil
+ )
+ }
+
+ @inline(__always)
+ private func getImagePath(for contact: CNContact, isThumbnail: Bool) -> String? {
+ let imageData = isThumbnail ? contact.thumbnailImageData : contact.imageData
+ guard let data = imageData else { return nil }
+
+ let fileName = "\(contact.identifier)_\(isThumbnail ? "thumb" : "full").jpg"
+ let fileURL = imageDirectory.appendingPathComponent(fileName)
+
+ if !FileManager.default.fileExists(atPath: fileURL.path) {
+ try? data.write(to: fileURL, options: .atomic)
+ }
+
+ return fileURL.path
+ }
+}
diff --git a/modules/ContactsNitroModule/nitro.json b/modules/ContactsNitroModule/nitro.json
new file mode 100644
index 000000000000..426f8486118a
--- /dev/null
+++ b/modules/ContactsNitroModule/nitro.json
@@ -0,0 +1,17 @@
+{
+ "cxxNamespace": ["contacts"],
+ "ios": {
+ "iosModuleName": "ContactsModule"
+ },
+ "android": {
+ "androidNamespace": ["contacts"],
+ "androidCxxLibName": "ContactsModule"
+ },
+ "autolinking": {
+ "ContactsModule": {
+ "swift": "HybridContactsModule",
+ "kotlin": "HybridContactsModule"
+ }
+ },
+ "ignorePaths": ["node_modules"]
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModule+autolinking.cmake b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModule+autolinking.cmake
new file mode 100644
index 000000000000..5478bc224b05
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModule+autolinking.cmake
@@ -0,0 +1,59 @@
+#
+# ContactsModule+autolinking.cmake
+# This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+# https://github.com/mrousavy/nitro
+# Copyright © 2024 Marc Rousavy @ Margelo
+#
+
+# This is a CMake file that adds all files generated by Nitrogen
+# to the current CMake project.
+#
+# To use it, add this to your CMakeLists.txt:
+# ```cmake
+# include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/ContactsModule+autolinking.cmake)
+# ```
+
+# Add all headers that were generated by Nitrogen
+include_directories(
+ "../nitrogen/generated/shared/c++"
+ "../nitrogen/generated/android/c++"
+ "../nitrogen/generated/android/"
+)
+
+# Add all .cpp sources that were generated by Nitrogen
+target_sources(
+ # CMake project name (Android C++ library name)
+ ContactsModule PRIVATE
+ # Autolinking Setup
+ ../nitrogen/generated/android/ContactsModuleOnLoad.cpp
+ # Shared Nitrogen C++ sources
+ ../nitrogen/generated/shared/c++/HybridContactsModuleSpec.cpp
+ # Android-specific Nitrogen C++ sources
+ ../nitrogen/generated/android/c++/JHybridContactsModuleSpec.cpp
+)
+
+# Add all libraries required by the generated specs
+find_package(fbjni REQUIRED) # <-- Used for communication between Java <-> C++
+find_package(ReactAndroid REQUIRED) # <-- Used to set up React Native bindings (e.g. CallInvoker/TurboModule)
+find_package(react-native-nitro-modules REQUIRED) # <-- Used to create all HybridObjects and use the Nitro core library
+
+# Link all libraries together
+target_link_libraries(
+ ContactsModule
+ fbjni::fbjni # <-- Facebook C++ JNI helpers
+ ReactAndroid::jsi # <-- RN: JSI
+ react-native-nitro-modules::NitroModules # <-- NitroModules Core :)
+)
+
+# Link react-native (different prefab between RN 0.75 and RN 0.76)
+if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
+ target_link_libraries(
+ ContactsModule
+ ReactAndroid::reactnative # <-- RN: Native Modules umbrella prefab
+ )
+else()
+ target_link_libraries(
+ ContactsModule
+ ReactAndroid::react_nativemodule_core # <-- RN: TurboModules Core
+ )
+endif()
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModule+autolinking.gradle b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModule+autolinking.gradle
new file mode 100644
index 000000000000..2d19cd2ced32
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModule+autolinking.gradle
@@ -0,0 +1,27 @@
+///
+/// ContactsModule+autolinking.gradle
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+/// This is a Gradle file that adds all files generated by Nitrogen
+/// to the current Gradle project.
+///
+/// To use it, add this to your build.gradle:
+/// ```gradle
+/// apply from: '../nitrogen/generated/android/ContactsModule+autolinking.gradle'
+/// ```
+
+logger.warn("[NitroModules] 🔥 ContactsModule is boosted by nitro!")
+
+android {
+ sourceSets {
+ main {
+ java.srcDirs += [
+ // Nitrogen files
+ "${project.projectDir}/../nitrogen/generated/android/kotlin"
+ ]
+ }
+ }
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.cpp b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.cpp
new file mode 100644
index 000000000000..156ea811e509
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.cpp
@@ -0,0 +1,42 @@
+///
+/// ContactsModuleOnLoad.cpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#include "ContactsModuleOnLoad.hpp"
+
+#include
+#include
+#include
+
+#include "JHybridContactsModuleSpec.hpp"
+#include
+#include
+
+namespace margelo::nitro::contacts {
+
+int initialize(JavaVM* vm) {
+ using namespace margelo::nitro;
+ using namespace margelo::nitro::contacts;
+ using namespace facebook;
+
+ return facebook::jni::initialize(vm, [] {
+ // Register native JNI methods
+ margelo::nitro::contacts::JHybridContactsModuleSpec::registerNatives();
+
+ // Register Nitro Hybrid Objects
+ HybridObjectRegistry::registerHybridObjectConstructor(
+ "ContactsModule",
+ []() -> std::shared_ptr {
+ static DefaultConstructableObject object("com/margelo/nitro/contacts/HybridContactsModule");
+ auto instance = object.create();
+ auto globalRef = jni::make_global(instance);
+ return JNISharedPtr::make_shared_from_jni(globalRef);
+ }
+ );
+ });
+}
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.hpp b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.hpp
new file mode 100644
index 000000000000..b71adaca07bf
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.hpp
@@ -0,0 +1,25 @@
+///
+/// ContactsModuleOnLoad.hpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#include
+#include
+
+namespace margelo::nitro::contacts {
+
+ /**
+ * Initializes the native (C++) part of ContactsModule, and autolinks all Hybrid Objects.
+ * Call this in your `JNI_OnLoad` function (probably inside `cpp-adapter.cpp`).
+ * Example:
+ * ```cpp (cpp-adapter.cpp)
+ * JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
+ * return margelo::nitro::contacts::initialize(vm);
+ * }
+ * ```
+ */
+ int initialize(JavaVM* vm);
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.kt b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.kt
new file mode 100644
index 000000000000..8b137891791f
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/ContactsModuleOnLoad.kt
@@ -0,0 +1 @@
+
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/c++/JContact.hpp b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JContact.hpp
new file mode 100644
index 000000000000..bbd5354163a2
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JContact.hpp
@@ -0,0 +1,114 @@
+///
+/// JContact.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
+#include "Contact.hpp"
+
+#include "JStringHolder.hpp"
+#include "StringHolder.hpp"
+#include
+#include
+#include
+
+namespace margelo::nitro::contacts {
+
+ using namespace facebook;
+
+ /**
+ * The C++ JNI bridge between the C++ struct "Contact" and the the Kotlin data class "Contact".
+ */
+ struct JContact final: public jni::JavaClass {
+ public:
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/contacts/Contact;";
+
+ public:
+ /**
+ * Convert this Java/Kotlin-based struct to the C++ struct Contact by copying all values to C++.
+ */
+ [[maybe_unused]]
+ Contact toCpp() const {
+ static const auto clazz = javaClassStatic();
+ static const auto fieldFirstName = clazz->getField("firstName");
+ jni::local_ref firstName = this->getFieldValue(fieldFirstName);
+ static const auto fieldLastName = clazz->getField("lastName");
+ jni::local_ref lastName = this->getFieldValue(fieldLastName);
+ static const auto fieldMiddleName = clazz->getField("middleName");
+ jni::local_ref middleName = this->getFieldValue(fieldMiddleName);
+ static const auto fieldPhoneNumbers = clazz->getField>("phoneNumbers");
+ jni::local_ref> phoneNumbers = this->getFieldValue(fieldPhoneNumbers);
+ static const auto fieldEmailAddresses = clazz->getField>("emailAddresses");
+ jni::local_ref> emailAddresses = this->getFieldValue(fieldEmailAddresses);
+ static const auto fieldImageData = clazz->getField("imageData");
+ jni::local_ref imageData = this->getFieldValue(fieldImageData);
+ static const auto fieldThumbnailImageData = clazz->getField("thumbnailImageData");
+ jni::local_ref thumbnailImageData = this->getFieldValue(fieldThumbnailImageData);
+ return Contact(
+ firstName != nullptr ? std::make_optional(firstName->toStdString()) : std::nullopt,
+ lastName != nullptr ? std::make_optional(lastName->toStdString()) : std::nullopt,
+ middleName != nullptr ? std::make_optional(middleName->toStdString()) : std::nullopt,
+ phoneNumbers != nullptr ? std::make_optional([&]() {
+ size_t __size = phoneNumbers->size();
+ std::vector __vector;
+ __vector.reserve(__size);
+ for (size_t __i = 0; __i < __size; __i++) {
+ auto __element = phoneNumbers->getElement(__i);
+ __vector.push_back(__element->toCpp());
+ }
+ return __vector;
+ }()) : std::nullopt,
+ emailAddresses != nullptr ? std::make_optional([&]() {
+ size_t __size = emailAddresses->size();
+ std::vector __vector;
+ __vector.reserve(__size);
+ for (size_t __i = 0; __i < __size; __i++) {
+ auto __element = emailAddresses->getElement(__i);
+ __vector.push_back(__element->toCpp());
+ }
+ return __vector;
+ }()) : std::nullopt,
+ imageData != nullptr ? std::make_optional(imageData->toStdString()) : std::nullopt,
+ thumbnailImageData != nullptr ? std::make_optional(thumbnailImageData->toStdString()) : std::nullopt
+ );
+ }
+
+ public:
+ /**
+ * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java.
+ */
+ [[maybe_unused]]
+ static jni::local_ref fromCpp(const Contact& value) {
+ return newInstance(
+ value.firstName.has_value() ? jni::make_jstring(value.firstName.value()) : nullptr,
+ value.lastName.has_value() ? jni::make_jstring(value.lastName.value()) : nullptr,
+ value.middleName.has_value() ? jni::make_jstring(value.middleName.value()) : nullptr,
+ value.phoneNumbers.has_value() ? [&]() {
+ size_t __size = value.phoneNumbers.value().size();
+ jni::local_ref> __array = jni::JArrayClass::newArray(__size);
+ for (size_t __i = 0; __i < __size; __i++) {
+ const auto& __element = value.phoneNumbers.value()[__i];
+ __array->setElement(__i, *JStringHolder::fromCpp(__element));
+ }
+ return __array;
+ }() : nullptr,
+ value.emailAddresses.has_value() ? [&]() {
+ size_t __size = value.emailAddresses.value().size();
+ jni::local_ref> __array = jni::JArrayClass::newArray(__size);
+ for (size_t __i = 0; __i < __size; __i++) {
+ const auto& __element = value.emailAddresses.value()[__i];
+ __array->setElement(__i, *JStringHolder::fromCpp(__element));
+ }
+ return __array;
+ }() : nullptr,
+ value.imageData.has_value() ? jni::make_jstring(value.imageData.value()) : nullptr,
+ value.thumbnailImageData.has_value() ? jni::make_jstring(value.thumbnailImageData.value()) : nullptr
+ );
+ }
+ };
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/c++/JContactFields.hpp b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JContactFields.hpp
new file mode 100644
index 000000000000..371b6607d105
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JContactFields.hpp
@@ -0,0 +1,76 @@
+///
+/// JContactFields.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
+#include "ContactFields.hpp"
+
+namespace margelo::nitro::contacts {
+
+ using namespace facebook;
+
+ /**
+ * The C++ JNI bridge between the C++ enum "ContactFields" and the the Kotlin enum "ContactFields".
+ */
+ struct JContactFields final: public jni::JavaClass {
+ public:
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/contacts/ContactFields;";
+
+ public:
+ /**
+ * Convert this Java/Kotlin-based enum to the C++ enum ContactFields.
+ */
+ [[maybe_unused]]
+ ContactFields toCpp() const {
+ static const auto clazz = javaClassStatic();
+ static const auto fieldOrdinal = clazz->getField("_ordinal");
+ int ordinal = this->getFieldValue(fieldOrdinal);
+ return static_cast(ordinal);
+ }
+
+ public:
+ /**
+ * Create a Java/Kotlin-based enum with the given C++ enum's value.
+ */
+ [[maybe_unused]]
+ static jni::alias_ref fromCpp(ContactFields value) {
+ static const auto clazz = javaClassStatic();
+ static const auto fieldFIRST_NAME = clazz->getStaticField("FIRST_NAME");
+ static const auto fieldLAST_NAME = clazz->getStaticField("LAST_NAME");
+ static const auto fieldMIDDLE_NAME = clazz->getStaticField("MIDDLE_NAME");
+ static const auto fieldPHONE_NUMBERS = clazz->getStaticField("PHONE_NUMBERS");
+ static const auto fieldEMAIL_ADDRESSES = clazz->getStaticField("EMAIL_ADDRESSES");
+ static const auto fieldIMAGE_DATA = clazz->getStaticField("IMAGE_DATA");
+ static const auto fieldTHUMBNAIL_IMAGE_DATA = clazz->getStaticField("THUMBNAIL_IMAGE_DATA");
+ static const auto fieldGIVEN_NAME_KEY = clazz->getStaticField("GIVEN_NAME_KEY");
+
+ switch (value) {
+ case ContactFields::FIRST_NAME:
+ return clazz->getStaticFieldValue(fieldFIRST_NAME);
+ case ContactFields::LAST_NAME:
+ return clazz->getStaticFieldValue(fieldLAST_NAME);
+ case ContactFields::MIDDLE_NAME:
+ return clazz->getStaticFieldValue(fieldMIDDLE_NAME);
+ case ContactFields::PHONE_NUMBERS:
+ return clazz->getStaticFieldValue(fieldPHONE_NUMBERS);
+ case ContactFields::EMAIL_ADDRESSES:
+ return clazz->getStaticFieldValue(fieldEMAIL_ADDRESSES);
+ case ContactFields::IMAGE_DATA:
+ return clazz->getStaticFieldValue(fieldIMAGE_DATA);
+ case ContactFields::THUMBNAIL_IMAGE_DATA:
+ return clazz->getStaticFieldValue(fieldTHUMBNAIL_IMAGE_DATA);
+ case ContactFields::GIVEN_NAME_KEY:
+ return clazz->getStaticFieldValue(fieldGIVEN_NAME_KEY);
+ default:
+ std::string stringValue = std::to_string(static_cast(value));
+ throw std::invalid_argument("Invalid enum value (" + stringValue + "!");
+ }
+ }
+ };
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/c++/JHybridContactsModuleSpec.cpp b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JHybridContactsModuleSpec.cpp
new file mode 100644
index 000000000000..e0505ee46d36
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JHybridContactsModuleSpec.cpp
@@ -0,0 +1,84 @@
+///
+/// JHybridContactsModuleSpec.cpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#include "JHybridContactsModuleSpec.hpp"
+
+// Forward declaration of `Contact` to properly resolve imports.
+namespace margelo::nitro::contacts { struct Contact; }
+// Forward declaration of `StringHolder` to properly resolve imports.
+namespace margelo::nitro::contacts { struct StringHolder; }
+// Forward declaration of `ContactFields` to properly resolve imports.
+namespace margelo::nitro::contacts { enum class ContactFields; }
+
+#include
+#include
+#include "Contact.hpp"
+#include
+#include "JContact.hpp"
+#include
+#include
+#include "StringHolder.hpp"
+#include "JStringHolder.hpp"
+#include "ContactFields.hpp"
+#include "JContactFields.hpp"
+
+namespace margelo::nitro::contacts {
+
+ jni::local_ref JHybridContactsModuleSpec::initHybrid(jni::alias_ref jThis) {
+ return makeCxxInstance(jThis);
+ }
+
+ void JHybridContactsModuleSpec::registerNatives() {
+ registerHybrid({
+ makeNativeMethod("initHybrid", JHybridContactsModuleSpec::initHybrid),
+ });
+ }
+
+ size_t JHybridContactsModuleSpec::getExternalMemorySize() noexcept {
+ static const auto method = _javaPart->getClass()->getMethod("getMemorySize");
+ return method(_javaPart);
+ }
+
+ // Properties
+
+
+ // Methods
+ std::shared_ptr>> JHybridContactsModuleSpec::getAll(const std::vector& keys) {
+ static const auto method = _javaPart->getClass()->getMethod(jni::alias_ref> /* keys */)>("getAll");
+ auto __result = method(_javaPart, [&]() {
+ size_t __size = keys.size();
+ jni::local_ref> __array = jni::JArrayClass::newArray(__size);
+ for (size_t __i = 0; __i < __size; __i++) {
+ const auto& __element = keys[__i];
+ __array->setElement(__i, *JContactFields::fromCpp(__element));
+ }
+ return __array;
+ }());
+ return [&]() {
+ auto __promise = Promise>::create();
+ __result->cthis()->addOnResolvedListener([=](const jni::alias_ref& __boxedResult) {
+ auto __result = jni::static_ref_cast>(__boxedResult);
+ __promise->resolve([&]() {
+ size_t __size = __result->size();
+ std::vector __vector;
+ __vector.reserve(__size);
+ for (size_t __i = 0; __i < __size; __i++) {
+ auto __element = __result->getElement(__i);
+ __vector.push_back(__element->toCpp());
+ }
+ return __vector;
+ }());
+ });
+ __result->cthis()->addOnRejectedListener([=](const jni::alias_ref& __throwable) {
+ jni::JniException __jniError(__throwable);
+ __promise->reject(std::make_exception_ptr(__jniError));
+ });
+ return __promise;
+ }();
+ }
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/c++/JHybridContactsModuleSpec.hpp b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JHybridContactsModuleSpec.hpp
new file mode 100644
index 000000000000..6b94d3be37e7
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JHybridContactsModuleSpec.hpp
@@ -0,0 +1,62 @@
+///
+/// HybridContactsModuleSpec.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
+#include
+#include "HybridContactsModuleSpec.hpp"
+
+
+
+
+namespace margelo::nitro::contacts {
+
+ using namespace facebook;
+
+ class JHybridContactsModuleSpec: public jni::HybridClass,
+ public virtual HybridContactsModuleSpec {
+ public:
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/contacts/HybridContactsModuleSpec;";
+ static jni::local_ref initHybrid(jni::alias_ref jThis);
+ static void registerNatives();
+
+ protected:
+ // C++ constructor (called from Java via `initHybrid()`)
+ explicit JHybridContactsModuleSpec(jni::alias_ref jThis) :
+ HybridObject(HybridContactsModuleSpec::TAG),
+ _javaPart(jni::make_global(jThis)) {}
+
+ public:
+ virtual ~JHybridContactsModuleSpec() {
+ // Hermes GC can destroy JS objects on a non-JNI Thread.
+ jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); });
+ }
+
+ public:
+ size_t getExternalMemorySize() noexcept override;
+
+ public:
+ inline const jni::global_ref& getJavaPart() const noexcept {
+ return _javaPart;
+ }
+
+ public:
+ // Properties
+
+
+ public:
+ // Methods
+ std::shared_ptr>> getAll(const std::vector& keys) override;
+
+ private:
+ friend HybridBase;
+ using HybridBase::HybridBase;
+ jni::global_ref _javaPart;
+ };
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/c++/JStringHolder.hpp b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JStringHolder.hpp
new file mode 100644
index 000000000000..29695fe48d58
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/c++/JStringHolder.hpp
@@ -0,0 +1,52 @@
+///
+/// JStringHolder.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
+#include "StringHolder.hpp"
+
+#include
+
+namespace margelo::nitro::contacts {
+
+ using namespace facebook;
+
+ /**
+ * The C++ JNI bridge between the C++ struct "StringHolder" and the the Kotlin data class "StringHolder".
+ */
+ struct JStringHolder final: public jni::JavaClass {
+ public:
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/contacts/StringHolder;";
+
+ public:
+ /**
+ * Convert this Java/Kotlin-based struct to the C++ struct StringHolder by copying all values to C++.
+ */
+ [[maybe_unused]]
+ StringHolder toCpp() const {
+ static const auto clazz = javaClassStatic();
+ static const auto fieldValue = clazz->getField("value");
+ jni::local_ref value = this->getFieldValue(fieldValue);
+ return StringHolder(
+ value->toStdString()
+ );
+ }
+
+ public:
+ /**
+ * Create a Java/Kotlin-based struct by copying all values from the given C++ struct to Java.
+ */
+ [[maybe_unused]]
+ static jni::local_ref fromCpp(const StringHolder& value) {
+ return newInstance(
+ jni::make_jstring(value.value)
+ );
+ }
+ };
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/Contact.kt b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/Contact.kt
new file mode 100644
index 000000000000..a6d9e59a2b2b
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/Contact.kt
@@ -0,0 +1,27 @@
+///
+/// Contact.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.contacts
+
+import androidx.annotation.Keep
+import com.facebook.proguard.annotations.DoNotStrip
+import com.margelo.nitro.core.*
+
+/**
+ * Represents the JavaScript object/struct "Contact".
+ */
+@DoNotStrip
+@Keep
+data class Contact(
+ val firstName: String?,
+ val lastName: String?,
+ val middleName: String?,
+ val phoneNumbers: Array?,
+ val emailAddresses: Array?,
+ val imageData: String?,
+ val thumbnailImageData: String?
+)
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/ContactFields.kt b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/ContactFields.kt
new file mode 100644
index 000000000000..841d6c82a32b
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/ContactFields.kt
@@ -0,0 +1,31 @@
+///
+/// ContactFields.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.contacts
+
+import androidx.annotation.Keep
+import com.facebook.proguard.annotations.DoNotStrip
+
+/**
+ * Represents the JavaScript enum/union "ContactFields".
+ */
+@DoNotStrip
+@Keep
+enum class ContactFields {
+ FIRST_NAME,
+ LAST_NAME,
+ MIDDLE_NAME,
+ PHONE_NUMBERS,
+ EMAIL_ADDRESSES,
+ IMAGE_DATA,
+ THUMBNAIL_IMAGE_DATA,
+ GIVEN_NAME_KEY;
+
+ @DoNotStrip
+ @Keep
+ private val _ordinal = ordinal
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/HybridContactsModuleSpec.kt b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/HybridContactsModuleSpec.kt
new file mode 100644
index 000000000000..63a118b8be57
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/HybridContactsModuleSpec.kt
@@ -0,0 +1,64 @@
+///
+/// HybridContactsModuleSpec.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.contacts
+
+import android.util.Log
+import androidx.annotation.Keep
+import com.facebook.jni.HybridData
+import com.facebook.proguard.annotations.DoNotStrip
+import com.margelo.nitro.core.*
+
+/**
+ * A Kotlin class representing the ContactsModule HybridObject.
+ * Implement this abstract class to create Kotlin-based instances of ContactsModule.
+ */
+@DoNotStrip
+@Keep
+@Suppress("RedundantSuppression", "KotlinJniMissingFunction", "PropertyName", "RedundantUnitReturnType", "unused")
+abstract class HybridContactsModuleSpec: HybridObject() {
+ @DoNotStrip
+ private var mHybridData: HybridData = initHybrid()
+
+ init {
+ // Pass this `HybridData` through to it's base class,
+ // to represent inheritance to JHybridObject on C++ side
+ super.updateNative(mHybridData)
+ }
+
+ /**
+ * Call from a child class to initialize HybridData with a child.
+ */
+ override fun updateNative(hybridData: HybridData) {
+ mHybridData = hybridData
+ }
+
+ // Properties
+
+
+ // Methods
+ @DoNotStrip
+ @Keep
+ abstract fun getAll(keys: Array): Promise>
+
+ private external fun initHybrid(): HybridData
+
+ companion object {
+ private const val TAG = "HybridContactsModuleSpec"
+ init {
+ try {
+ Log.i(TAG, "Loading ContactsModule C++ library...")
+ System.loadLibrary("ContactsModule")
+ Log.i(TAG, "Successfully loaded ContactsModule C++ library!")
+ } catch (e: Error) {
+ Log.e(TAG, "Failed to load ContactsModule C++ library! Is it properly installed and linked? " +
+ "Is the name correct? (see `CMakeLists.txt`, at `add_library(...)`)", e)
+ throw e
+ }
+ }
+ }
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/StringHolder.kt b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/StringHolder.kt
new file mode 100644
index 000000000000..b6af53e53217
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/android/kotlin/com/margelo/nitro/contacts/StringHolder.kt
@@ -0,0 +1,21 @@
+///
+/// StringHolder.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.contacts
+
+import androidx.annotation.Keep
+import com.facebook.proguard.annotations.DoNotStrip
+import com.margelo.nitro.core.*
+
+/**
+ * Represents the JavaScript object/struct "StringHolder".
+ */
+@DoNotStrip
+@Keep
+data class StringHolder(
+ val value: String
+)
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule+autolinking.rb b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule+autolinking.rb
new file mode 100644
index 000000000000..35bc19c47bf7
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule+autolinking.rb
@@ -0,0 +1,58 @@
+#
+# ContactsModule+autolinking.rb
+# This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+# https://github.com/mrousavy/nitro
+# Copyright © 2024 Marc Rousavy @ Margelo
+#
+
+# This is a Ruby script that adds all files generated by Nitrogen
+# to the given podspec.
+#
+# To use it, add this to your .podspec:
+# ```ruby
+# Pod::Spec.new do |spec|
+# # ...
+#
+# # Add all files generated by Nitrogen
+# load 'nitrogen/generated/ios/ContactsModule+autolinking.rb'
+# add_nitrogen_files(spec)
+# end
+# ```
+
+def add_nitrogen_files(spec)
+ Pod::UI.puts "[NitroModules] 🔥 ContactsModule is boosted by nitro!"
+
+ spec.dependency "NitroModules"
+
+ current_source_files = Array(spec.attributes_hash['source_files'])
+ spec.source_files = current_source_files + [
+ # Generated cross-platform specs
+ "nitrogen/generated/shared/**/*.{h,hpp,c,cpp,swift}",
+ # Generated bridges for the cross-platform specs
+ "nitrogen/generated/ios/**/*.{h,hpp,c,cpp,mm,swift}",
+ ]
+
+ current_public_header_files = Array(spec.attributes_hash['public_header_files'])
+ spec.public_header_files = current_public_header_files + [
+ # Generated specs
+ "nitrogen/generated/shared/**/*.{h,hpp}",
+ # Swift to C++ bridging helpers
+ "nitrogen/generated/ios/ContactsModule-Swift-Cxx-Bridge.hpp"
+ ]
+
+ current_private_header_files = Array(spec.attributes_hash['private_header_files'])
+ spec.private_header_files = current_private_header_files + [
+ # iOS specific specs
+ "nitrogen/generated/ios/c++/**/*.{h,hpp}",
+ ]
+
+ current_pod_target_xcconfig = spec.attributes_hash['pod_target_xcconfig'] || {}
+ spec.pod_target_xcconfig = current_pod_target_xcconfig.merge({
+ # Use C++ 20
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
+ # Enables C++ <-> Swift interop (by default it's only C)
+ "SWIFT_OBJC_INTEROP_MODE" => "objcxx",
+ # Enables stricter modular headers
+ "DEFINES_MODULE" => "YES",
+ })
+end
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Bridge.cpp b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Bridge.cpp
new file mode 100644
index 000000000000..4746dbadaa18
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Bridge.cpp
@@ -0,0 +1,33 @@
+///
+/// ContactsModule-Swift-Cxx-Bridge.cpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#include "ContactsModule-Swift-Cxx-Bridge.hpp"
+
+// Include C++ implementation defined types
+#include "ContactsModule-Swift-Cxx-Umbrella.hpp"
+#include "HybridContactsModuleSpecSwift.hpp"
+#include
+
+namespace margelo::nitro::contacts::bridge::swift {
+
+ // pragma MARK: std::shared_ptr
+ std::shared_ptr create_std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_(void* _Nonnull swiftUnsafePointer) {
+ ContactsModule::HybridContactsModuleSpecCxx swiftPart = ContactsModule::HybridContactsModuleSpecCxxUnsafe::fromUnsafe(swiftUnsafePointer);
+ return HybridContext::getOrCreate(swiftPart);
+ }
+ void* _Nonnull get_std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_(std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_ cppType) {
+ std::shared_ptr swiftWrapper = std::dynamic_pointer_cast(cppType);
+ #ifdef NITRO_DEBUG
+ if (swiftWrapper == nullptr) [[unlikely]] {
+ throw std::runtime_error("Class \"HybridContactsModuleSpec\" is not implemented in Swift!");
+ }
+ #endif
+ ContactsModule::HybridContactsModuleSpecCxx swiftPart = swiftWrapper->getSwiftPart();
+ return ContactsModule::HybridContactsModuleSpecCxxUnsafe::toUnsafe(swiftPart);
+ }
+
+} // namespace margelo::nitro::contacts::bridge::swift
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Bridge.hpp b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Bridge.hpp
new file mode 100644
index 000000000000..76d584613df2
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Bridge.hpp
@@ -0,0 +1,167 @@
+///
+/// ContactsModule-Swift-Cxx-Bridge.hpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#pragma once
+
+// Forward declarations of C++ defined types
+// Forward declaration of `ContactFields` to properly resolve imports.
+namespace margelo::nitro::contacts { enum class ContactFields; }
+// Forward declaration of `Contact` to properly resolve imports.
+namespace margelo::nitro::contacts { struct Contact; }
+// Forward declaration of `HybridContactsModuleSpec` to properly resolve imports.
+namespace margelo::nitro::contacts { class HybridContactsModuleSpec; }
+// Forward declaration of `StringHolder` to properly resolve imports.
+namespace margelo::nitro::contacts { struct StringHolder; }
+
+// Forward declarations of Swift defined types
+// Forward declaration of `HybridContactsModuleSpecCxx` to properly resolve imports.
+namespace ContactsModule { class HybridContactsModuleSpecCxx; }
+
+// Include C++ defined types
+#include "Contact.hpp"
+#include "ContactFields.hpp"
+#include "HybridContactsModuleSpec.hpp"
+#include "StringHolder.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * Contains specialized versions of C++ templated types so they can be accessed from Swift,
+ * as well as helper functions to interact with those C++ types from Swift.
+ */
+namespace margelo::nitro::contacts::bridge::swift {
+
+ // pragma MARK: std::optional
+ /**
+ * Specialized version of `std::optional`.
+ */
+ using std__optional_std__string_ = std::optional;
+ inline std::optional create_std__optional_std__string_(const std::string& value) {
+ return std::optional(value);
+ }
+
+ // pragma MARK: std::vector
+ /**
+ * Specialized version of `std::vector`.
+ */
+ using std__vector_StringHolder_ = std::vector;
+ inline std::vector create_std__vector_StringHolder_(size_t size) {
+ std::vector vector;
+ vector.reserve(size);
+ return vector;
+ }
+
+ // pragma MARK: std::optional>
+ /**
+ * Specialized version of `std::optional>`.
+ */
+ using std__optional_std__vector_StringHolder__ = std::optional>;
+ inline std::optional> create_std__optional_std__vector_StringHolder__(const std::vector& value) {
+ return std::optional>(value);
+ }
+
+ // pragma MARK: std::vector
+ /**
+ * Specialized version of `std::vector`.
+ */
+ using std__vector_Contact_ = std::vector;
+ inline std::vector create_std__vector_Contact_(size_t size) {
+ std::vector vector;
+ vector.reserve(size);
+ return vector;
+ }
+
+ // pragma MARK: std::shared_ptr>>
+ /**
+ * Specialized version of `std::shared_ptr>>`.
+ */
+ using std__shared_ptr_Promise_std__vector_Contact___ = std::shared_ptr>>;
+ inline std::shared_ptr>> create_std__shared_ptr_Promise_std__vector_Contact___() {
+ return Promise>::create();
+ }
+
+ // pragma MARK: std::function& /* result */)>
+ /**
+ * Specialized version of `std::function&)>`.
+ */
+ using Func_void_std__vector_Contact_ = std::function& /* result */)>;
+ /**
+ * Wrapper class for a `std::function& / * result * /)>`, this can be used from Swift.
+ */
+ class Func_void_std__vector_Contact__Wrapper final {
+ public:
+ explicit Func_void_std__vector_Contact__Wrapper(const std::function& /* result */)>& func): _function(func) {}
+ explicit Func_void_std__vector_Contact__Wrapper(std::function& /* result */)>&& func): _function(std::move(func)) {}
+ inline void call(std::vector result) const {
+ _function(result);
+ }
+ private:
+ std::function& /* result */)> _function;
+ };
+ inline Func_void_std__vector_Contact_ create_Func_void_std__vector_Contact_(void* _Nonnull closureHolder, void(* _Nonnull call)(void* _Nonnull /* closureHolder */, std::vector), void(* _Nonnull destroy)(void* _Nonnull)) {
+ std::shared_ptr sharedClosureHolder(closureHolder, destroy);
+ return Func_void_std__vector_Contact_([sharedClosureHolder, call](const std::vector& result) -> void {
+ call(sharedClosureHolder.get(), result);
+ });
+ }
+ inline std::shared_ptr share_Func_void_std__vector_Contact_(const Func_void_std__vector_Contact_& value) {
+ return std::make_shared(value);
+ }
+
+ // pragma MARK: std::function
+ /**
+ * Specialized version of `std::function`.
+ */
+ using Func_void_std__exception_ptr = std::function;
+ /**
+ * Wrapper class for a `std::function`, this can be used from Swift.
+ */
+ class Func_void_std__exception_ptr_Wrapper final {
+ public:
+ explicit Func_void_std__exception_ptr_Wrapper(const std::function& func): _function(func) {}
+ explicit Func_void_std__exception_ptr_Wrapper(std::function&& func): _function(std::move(func)) {}
+ inline void call(std::exception_ptr error) const {
+ _function(error);
+ }
+ private:
+ std::function _function;
+ };
+ inline Func_void_std__exception_ptr create_Func_void_std__exception_ptr(void* _Nonnull closureHolder, void(* _Nonnull call)(void* _Nonnull /* closureHolder */, std::exception_ptr), void(* _Nonnull destroy)(void* _Nonnull)) {
+ std::shared_ptr sharedClosureHolder(closureHolder, destroy);
+ return Func_void_std__exception_ptr([sharedClosureHolder, call](const std::exception_ptr& error) -> void {
+ call(sharedClosureHolder.get(), error);
+ });
+ }
+ inline std::shared_ptr share_Func_void_std__exception_ptr(const Func_void_std__exception_ptr& value) {
+ return std::make_shared(value);
+ }
+
+ // pragma MARK: std::vector
+ /**
+ * Specialized version of `std::vector`.
+ */
+ using std__vector_ContactFields_ = std::vector;
+ inline std::vector create_std__vector_ContactFields_(size_t size) {
+ std::vector vector;
+ vector.reserve(size);
+ return vector;
+ }
+
+ // pragma MARK: std::shared_ptr
+ /**
+ * Specialized version of `std::shared_ptr`.
+ */
+ using std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_ = std::shared_ptr;
+ std::shared_ptr create_std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_(void* _Nonnull swiftUnsafePointer);
+ void* _Nonnull get_std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_(std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_ cppType);
+
+} // namespace margelo::nitro::contacts::bridge::swift
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Umbrella.hpp b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Umbrella.hpp
new file mode 100644
index 000000000000..6f38d7c7e417
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModule-Swift-Cxx-Umbrella.hpp
@@ -0,0 +1,54 @@
+///
+/// ContactsModule-Swift-Cxx-Umbrella.hpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#pragma once
+
+// Forward declarations of C++ defined types
+// Forward declaration of `ContactFields` to properly resolve imports.
+namespace margelo::nitro::contacts { enum class ContactFields; }
+// Forward declaration of `Contact` to properly resolve imports.
+namespace margelo::nitro::contacts { struct Contact; }
+// Forward declaration of `HybridContactsModuleSpec` to properly resolve imports.
+namespace margelo::nitro::contacts { class HybridContactsModuleSpec; }
+// Forward declaration of `StringHolder` to properly resolve imports.
+namespace margelo::nitro::contacts { struct StringHolder; }
+
+// Include C++ defined types
+#include "Contact.hpp"
+#include "ContactFields.hpp"
+#include "HybridContactsModuleSpec.hpp"
+#include "StringHolder.hpp"
+#include
+#include
+#include
+#include
+#include
+
+// C++ helpers for Swift
+#include "ContactsModule-Swift-Cxx-Bridge.hpp"
+
+// Common C++ types used in Swift
+#include
+#include
+#include
+#include
+
+// Forward declarations of Swift defined types
+// Forward declaration of `HybridContactsModuleSpecCxx` to properly resolve imports.
+namespace ContactsModule { class HybridContactsModuleSpecCxx; }
+
+// Include Swift defined types
+#if __has_include("ContactsModule-Swift.h")
+// This header is generated by Xcode/Swift on every app build.
+// If it cannot be found, make sure the Swift module's name (= podspec name) is actually "ContactsModule".
+#include "ContactsModule-Swift.h"
+// Same as above, but used when building with frameworks (`use_frameworks`)
+#elif __has_include()
+#include
+#else
+#error ContactsModule's autogenerated Swift header cannot be found! Make sure the Swift module's name (= podspec name) is actually "ContactsModule", and try building the app first.
+#endif
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModuleAutolinking.mm b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModuleAutolinking.mm
new file mode 100644
index 000000000000..e769fb6a6806
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModuleAutolinking.mm
@@ -0,0 +1,33 @@
+///
+/// ContactsModuleAutolinking.mm
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#import
+#import
+#import "ContactsModule-Swift-Cxx-Umbrella.hpp"
+#import
+
+#include "HybridContactsModuleSpecSwift.hpp"
+
+@interface ContactsModuleAutolinking : NSObject
+@end
+
+@implementation ContactsModuleAutolinking
+
++ (void) load {
+ using namespace margelo::nitro;
+ using namespace margelo::nitro::contacts;
+
+ HybridObjectRegistry::registerHybridObjectConstructor(
+ "ContactsModule",
+ []() -> std::shared_ptr {
+ std::shared_ptr hybridObject = ContactsModule::ContactsModuleAutolinking::createContactsModule();
+ return hybridObject;
+ }
+ );
+}
+
+@end
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModuleAutolinking.swift b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModuleAutolinking.swift
new file mode 100644
index 000000000000..15d9d9b9064e
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/ContactsModuleAutolinking.swift
@@ -0,0 +1,26 @@
+///
+/// ContactsModuleAutolinking.swift
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+public final class ContactsModuleAutolinking {
+ public typealias bridge = margelo.nitro.contacts.bridge.swift
+
+ /**
+ * Creates an instance of a Swift class that implements `HybridContactsModuleSpec`,
+ * and wraps it in a Swift class that can directly interop with C++ (`HybridContactsModuleSpecCxx`)
+ *
+ * This is generated by Nitrogen and will initialize the class specified
+ * in the `"autolinking"` property of `nitro.json` (in this case, `HybridContactsModule`).
+ */
+ public static func createContactsModule() -> bridge.std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_ {
+ let hybridObject = HybridContactsModule()
+ return { () -> bridge.std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_ in
+ let __cxxWrapped = HybridContactsModuleSpecCxx(hybridObject)
+ let __pointer = HybridContactsModuleSpecCxxUnsafe.toUnsafe(__cxxWrapped)
+ return bridge.create_std__shared_ptr_margelo__nitro__contacts__HybridContactsModuleSpec_(__pointer)
+ }()
+ }
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/c++/HybridContactsModuleSpecSwift.cpp b/modules/ContactsNitroModule/nitrogen/generated/ios/c++/HybridContactsModuleSpecSwift.cpp
new file mode 100644
index 000000000000..71151f3c1883
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/c++/HybridContactsModuleSpecSwift.cpp
@@ -0,0 +1,11 @@
+///
+/// HybridContactsModuleSpecSwift.cpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#include "HybridContactsModuleSpecSwift.hpp"
+
+namespace margelo::nitro::contacts {
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/c++/HybridContactsModuleSpecSwift.hpp b/modules/ContactsNitroModule/nitrogen/generated/ios/c++/HybridContactsModuleSpecSwift.hpp
new file mode 100644
index 000000000000..dbb4fe829dc2
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/c++/HybridContactsModuleSpecSwift.hpp
@@ -0,0 +1,82 @@
+///
+/// HybridContactsModuleSpecSwift.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 "HybridContactsModuleSpec.hpp"
+
+// Forward declaration of `HybridContactsModuleSpecCxx` to properly resolve imports.
+namespace ContactsModule { class HybridContactsModuleSpecCxx; }
+
+// Forward declaration of `Contact` to properly resolve imports.
+namespace margelo::nitro::contacts { struct Contact; }
+// Forward declaration of `StringHolder` to properly resolve imports.
+namespace margelo::nitro::contacts { struct StringHolder; }
+// Forward declaration of `ContactFields` to properly resolve imports.
+namespace margelo::nitro::contacts { enum class ContactFields; }
+
+#include
+#include
+#include "Contact.hpp"
+#include
+#include
+#include "StringHolder.hpp"
+#include "ContactFields.hpp"
+
+#if __has_include()
+#include
+#else
+#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
+#endif
+
+#include "ContactsModule-Swift-Cxx-Umbrella.hpp"
+
+namespace margelo::nitro::contacts {
+
+ /**
+ * The C++ part of HybridContactsModuleSpecCxx.swift.
+ *
+ * HybridContactsModuleSpecSwift (C++) accesses HybridContactsModuleSpecCxx (Swift), and might
+ * contain some additional bridging code for C++ <> Swift interop.
+ *
+ * Since this obviously introduces an overhead, I hope at some point in
+ * the future, HybridContactsModuleSpecCxx can directly inherit from the C++ class HybridContactsModuleSpec
+ * to simplify the whole structure and memory management.
+ */
+ class HybridContactsModuleSpecSwift: public virtual HybridContactsModuleSpec {
+ public:
+ // Constructor from a Swift instance
+ explicit HybridContactsModuleSpecSwift(const ContactsModule::HybridContactsModuleSpecCxx& swiftPart):
+ HybridObject(HybridContactsModuleSpec::TAG),
+ _swiftPart(swiftPart) { }
+
+ public:
+ // Get the Swift part
+ inline ContactsModule::HybridContactsModuleSpecCxx getSwiftPart() noexcept { return _swiftPart; }
+
+ public:
+ // Get memory pressure
+ inline size_t getExternalMemorySize() noexcept override {
+ return _swiftPart.getMemorySize();
+ }
+
+ public:
+ // Properties
+
+
+ public:
+ // Methods
+ inline std::shared_ptr>> getAll(const std::vector& keys) override {
+ auto __result = _swiftPart.getAll(keys);
+ return __result;
+ }
+
+ private:
+ ContactsModule::HybridContactsModuleSpecCxx _swiftPart;
+ };
+
+} // namespace margelo::nitro::contacts
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/swift/Contact.swift b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/Contact.swift
new file mode 100644
index 000000000000..404d6ba86b25
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/Contact.swift
@@ -0,0 +1,251 @@
+///
+/// Contact.swift
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+import NitroModules
+
+/**
+ * Represents an instance of `Contact`, backed by a C++ struct.
+ */
+public typealias Contact = margelo.nitro.contacts.Contact
+
+public extension Contact {
+ private typealias bridge = margelo.nitro.contacts.bridge.swift
+
+ /**
+ * Create a new instance of `Contact`.
+ */
+ init(firstName: String?, lastName: String?, middleName: String?, phoneNumbers: [StringHolder]?, emailAddresses: [StringHolder]?, imageData: String?, thumbnailImageData: String?) {
+ self.init({ () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = firstName {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }(), { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = lastName {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }(), { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = middleName {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }(), { () -> bridge.std__optional_std__vector_StringHolder__ in
+ if let __unwrappedValue = phoneNumbers {
+ return bridge.create_std__optional_std__vector_StringHolder__({ () -> bridge.std__vector_StringHolder_ in
+ var __vector = bridge.create_std__vector_StringHolder_(__unwrappedValue.count)
+ for __item in __unwrappedValue {
+ __vector.push_back(__item)
+ }
+ return __vector
+ }())
+ } else {
+ return .init()
+ }
+ }(), { () -> bridge.std__optional_std__vector_StringHolder__ in
+ if let __unwrappedValue = emailAddresses {
+ return bridge.create_std__optional_std__vector_StringHolder__({ () -> bridge.std__vector_StringHolder_ in
+ var __vector = bridge.create_std__vector_StringHolder_(__unwrappedValue.count)
+ for __item in __unwrappedValue {
+ __vector.push_back(__item)
+ }
+ return __vector
+ }())
+ } else {
+ return .init()
+ }
+ }(), { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = imageData {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }(), { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = thumbnailImageData {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }())
+ }
+
+ var firstName: String? {
+ @inline(__always)
+ get {
+ return { () -> String? in
+ if let __unwrapped = self.__firstName.value {
+ return String(__unwrapped)
+ } else {
+ return nil
+ }
+ }()
+ }
+ @inline(__always)
+ set {
+ self.__firstName = { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = newValue {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }()
+ }
+ }
+
+ var lastName: String? {
+ @inline(__always)
+ get {
+ return { () -> String? in
+ if let __unwrapped = self.__lastName.value {
+ return String(__unwrapped)
+ } else {
+ return nil
+ }
+ }()
+ }
+ @inline(__always)
+ set {
+ self.__lastName = { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = newValue {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }()
+ }
+ }
+
+ var middleName: String? {
+ @inline(__always)
+ get {
+ return { () -> String? in
+ if let __unwrapped = self.__middleName.value {
+ return String(__unwrapped)
+ } else {
+ return nil
+ }
+ }()
+ }
+ @inline(__always)
+ set {
+ self.__middleName = { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = newValue {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }()
+ }
+ }
+
+ var phoneNumbers: [StringHolder]? {
+ @inline(__always)
+ get {
+ return { () -> [StringHolder]? in
+ if let __unwrapped = self.__phoneNumbers.value {
+ return __unwrapped.map({ __item in __item })
+ } else {
+ return nil
+ }
+ }()
+ }
+ @inline(__always)
+ set {
+ self.__phoneNumbers = { () -> bridge.std__optional_std__vector_StringHolder__ in
+ if let __unwrappedValue = newValue {
+ return bridge.create_std__optional_std__vector_StringHolder__({ () -> bridge.std__vector_StringHolder_ in
+ var __vector = bridge.create_std__vector_StringHolder_(__unwrappedValue.count)
+ for __item in __unwrappedValue {
+ __vector.push_back(__item)
+ }
+ return __vector
+ }())
+ } else {
+ return .init()
+ }
+ }()
+ }
+ }
+
+ var emailAddresses: [StringHolder]? {
+ @inline(__always)
+ get {
+ return { () -> [StringHolder]? in
+ if let __unwrapped = self.__emailAddresses.value {
+ return __unwrapped.map({ __item in __item })
+ } else {
+ return nil
+ }
+ }()
+ }
+ @inline(__always)
+ set {
+ self.__emailAddresses = { () -> bridge.std__optional_std__vector_StringHolder__ in
+ if let __unwrappedValue = newValue {
+ return bridge.create_std__optional_std__vector_StringHolder__({ () -> bridge.std__vector_StringHolder_ in
+ var __vector = bridge.create_std__vector_StringHolder_(__unwrappedValue.count)
+ for __item in __unwrappedValue {
+ __vector.push_back(__item)
+ }
+ return __vector
+ }())
+ } else {
+ return .init()
+ }
+ }()
+ }
+ }
+
+ var imageData: String? {
+ @inline(__always)
+ get {
+ return { () -> String? in
+ if let __unwrapped = self.__imageData.value {
+ return String(__unwrapped)
+ } else {
+ return nil
+ }
+ }()
+ }
+ @inline(__always)
+ set {
+ self.__imageData = { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = newValue {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }()
+ }
+ }
+
+ var thumbnailImageData: String? {
+ @inline(__always)
+ get {
+ return { () -> String? in
+ if let __unwrapped = self.__thumbnailImageData.value {
+ return String(__unwrapped)
+ } else {
+ return nil
+ }
+ }()
+ }
+ @inline(__always)
+ set {
+ self.__thumbnailImageData = { () -> bridge.std__optional_std__string_ in
+ if let __unwrappedValue = newValue {
+ return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
+ } else {
+ return .init()
+ }
+ }()
+ }
+ }
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/swift/ContactFields.swift b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/ContactFields.swift
new file mode 100644
index 000000000000..ce38940795d9
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/ContactFields.swift
@@ -0,0 +1,64 @@
+///
+/// ContactFields.swift
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+/**
+ * Represents the JS union `ContactFields`, backed by a C++ enum.
+ */
+public typealias ContactFields = margelo.nitro.contacts.ContactFields
+
+public extension ContactFields {
+ /**
+ * Get a ContactFields for the given String value, or
+ * return `nil` if the given value was invalid/unknown.
+ */
+ init?(fromString string: String) {
+ switch string {
+ case "FIRST_NAME":
+ self = .firstName
+ case "LAST_NAME":
+ self = .lastName
+ case "MIDDLE_NAME":
+ self = .middleName
+ case "PHONE_NUMBERS":
+ self = .phoneNumbers
+ case "EMAIL_ADDRESSES":
+ self = .emailAddresses
+ case "IMAGE_DATA":
+ self = .imageData
+ case "THUMBNAIL_IMAGE_DATA":
+ self = .thumbnailImageData
+ case "GIVEN_NAME_KEY":
+ self = .givenNameKey
+ default:
+ return nil
+ }
+ }
+
+ /**
+ * Get the String value this ContactFields represents.
+ */
+ var stringValue: String {
+ switch self {
+ case .firstName:
+ return "FIRST_NAME"
+ case .lastName:
+ return "LAST_NAME"
+ case .middleName:
+ return "MIDDLE_NAME"
+ case .phoneNumbers:
+ return "PHONE_NUMBERS"
+ case .emailAddresses:
+ return "EMAIL_ADDRESSES"
+ case .imageData:
+ return "IMAGE_DATA"
+ case .thumbnailImageData:
+ return "THUMBNAIL_IMAGE_DATA"
+ case .givenNameKey:
+ return "GIVEN_NAME_KEY"
+ }
+ }
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/swift/HybridContactsModuleSpec.swift b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/HybridContactsModuleSpec.swift
new file mode 100644
index 000000000000..611110efca1d
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/HybridContactsModuleSpec.swift
@@ -0,0 +1,36 @@
+///
+/// HybridContactsModuleSpec.swift
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+import Foundation
+import NitroModules
+
+/**
+ * A Swift protocol representing the ContactsModule HybridObject.
+ * Implement this protocol to create Swift-based instances of ContactsModule.
+ *
+ * When implementing this protocol, make sure to initialize `hybridContext` - example:
+ * ```
+ * public class HybridContactsModule : HybridContactsModuleSpec {
+ * // Initialize HybridContext
+ * var hybridContext = margelo.nitro.HybridContext()
+ *
+ * // Return size of the instance to inform JS GC about memory pressure
+ * var memorySize: Int {
+ * return getSizeOf(self)
+ * }
+ *
+ * // ...
+ * }
+ * ```
+ */
+public protocol HybridContactsModuleSpec: AnyObject, HybridObjectSpec {
+ // Properties
+
+
+ // Methods
+ func getAll(keys: [ContactFields]) throws -> Promise<[Contact]>
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/swift/HybridContactsModuleSpecCxx.swift b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/HybridContactsModuleSpecCxx.swift
new file mode 100644
index 000000000000..156cdf86bd7f
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/HybridContactsModuleSpecCxx.swift
@@ -0,0 +1,123 @@
+///
+/// HybridContactsModuleSpecCxx.swift
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+import Foundation
+import NitroModules
+
+/**
+ * Helper class for converting instances of `HybridContactsModuleSpecCxx` from- and to unsafe pointers.
+ * This is useful to pass Swift classes to C++, without having to strongly type the C++ function signature.
+ * The actual Swift type can be included in the .cpp file, without having to forward-declare anything in .hpp.
+ */
+public final class HybridContactsModuleSpecCxxUnsafe {
+ /**
+ * Casts a `HybridContactsModuleSpecCxx` instance to a retained unsafe raw pointer.
+ * This acquires one additional strong reference on the object!
+ */
+ public static func toUnsafe(_ instance: HybridContactsModuleSpecCxx) -> UnsafeMutableRawPointer {
+ return Unmanaged.passRetained(instance).toOpaque()
+ }
+
+ /**
+ * Casts an unsafe pointer to a `HybridContactsModuleSpecCxx`.
+ * The pointer has to be a retained opaque `Unmanaged`.
+ * This removes one strong reference from the object!
+ */
+ public static func fromUnsafe(_ pointer: UnsafeMutableRawPointer) -> HybridContactsModuleSpecCxx {
+ return Unmanaged.fromOpaque(pointer).takeRetainedValue()
+ }
+}
+
+/**
+ * A class implementation that bridges HybridContactsModuleSpec over to C++.
+ * In C++, we cannot use Swift protocols - so we need to wrap it in a class to make it strongly defined.
+ *
+ * Also, some Swift types need to be bridged with special handling:
+ * - Enums need to be wrapped in Structs, otherwise they cannot be accessed bi-directionally (Swift bug: https://github.com/swiftlang/swift/issues/75330)
+ * - Other HybridObjects need to be wrapped/unwrapped from the Swift TCxx wrapper
+ * - Throwing methods need to be wrapped with a Result type, as exceptions cannot be propagated to C++
+ */
+public class HybridContactsModuleSpecCxx {
+ /**
+ * The Swift <> C++ bridge's namespace (`margelo::nitro::contacts::bridge::swift`)
+ * from `ContactsModule-Swift-Cxx-Bridge.hpp`.
+ * This contains specialized C++ templates, and C++ helper functions that can be accessed from Swift.
+ */
+ public typealias bridge = margelo.nitro.contacts.bridge.swift
+
+ /**
+ * Holds an instance of the `HybridContactsModuleSpec` Swift protocol.
+ */
+ private var __implementation: any HybridContactsModuleSpec
+
+ /**
+ * Create a new `HybridContactsModuleSpecCxx` that wraps the given `HybridContactsModuleSpec`.
+ * All properties and methods bridge to C++ types.
+ */
+ public init(_ implementation: some HybridContactsModuleSpec) {
+ self.__implementation = implementation
+ /* no base class */
+ }
+
+ /**
+ * Get the actual `HybridContactsModuleSpec` instance this class wraps.
+ */
+ @inline(__always)
+ public func getHybridContactsModuleSpec() -> any HybridContactsModuleSpec {
+ return __implementation
+ }
+
+ /**
+ * Contains a (weak) reference to the C++ HybridObject to cache it.
+ */
+ public var hybridContext: margelo.nitro.HybridContext {
+ @inline(__always)
+ get {
+ return self.__implementation.hybridContext
+ }
+ @inline(__always)
+ set {
+ self.__implementation.hybridContext = newValue
+ }
+ }
+
+ /**
+ * Get the memory size of the Swift class (plus size of any other allocations)
+ * so the JS VM can properly track it and garbage-collect the JS object if needed.
+ */
+ @inline(__always)
+ public var memorySize: Int {
+ return self.__implementation.memorySize
+ }
+
+ // Properties
+
+
+ // Methods
+ @inline(__always)
+ public func getAll(keys: bridge.std__vector_ContactFields_) -> bridge.std__shared_ptr_Promise_std__vector_Contact___ {
+ do {
+ let __result = try self.__implementation.getAll(keys: keys.map({ __item in __item }))
+ return { () -> bridge.std__shared_ptr_Promise_std__vector_Contact___ in
+ let __promise = bridge.create_std__shared_ptr_Promise_std__vector_Contact___()
+ __result
+ .then({ __result in __promise.pointee.resolve({ () -> bridge.std__vector_Contact_ in
+ var __vector = bridge.create_std__vector_Contact_(__result.count)
+ for __item in __result {
+ __vector.push_back(__item)
+ }
+ return __vector
+ }()) })
+ .catch({ __error in __promise.pointee.reject(__error.toCpp()) })
+ return __promise
+ }()
+ } 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))")
+ }
+ }
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/ios/swift/StringHolder.swift b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/StringHolder.swift
new file mode 100644
index 000000000000..477279082456
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/ios/swift/StringHolder.swift
@@ -0,0 +1,35 @@
+///
+/// StringHolder.swift
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+import NitroModules
+
+/**
+ * Represents an instance of `StringHolder`, backed by a C++ struct.
+ */
+public typealias StringHolder = margelo.nitro.contacts.StringHolder
+
+public extension StringHolder {
+ private typealias bridge = margelo.nitro.contacts.bridge.swift
+
+ /**
+ * Create a new instance of `StringHolder`.
+ */
+ init(value: String) {
+ self.init(std.string(value))
+ }
+
+ var value: String {
+ @inline(__always)
+ get {
+ return String(self.__value)
+ }
+ @inline(__always)
+ set {
+ self.__value = std.string(newValue)
+ }
+ }
+}
diff --git a/modules/ContactsNitroModule/nitrogen/generated/shared/c++/Contact.hpp b/modules/ContactsNitroModule/nitrogen/generated/shared/c++/Contact.hpp
new file mode 100644
index 000000000000..6e4a5bd0c27e
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/shared/c++/Contact.hpp
@@ -0,0 +1,96 @@
+///
+/// Contact.hpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#pragma once
+
+#if __has_include()
+#include
+#else
+#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
+#endif
+#if __has_include()
+#include
+#else
+#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
+#endif
+
+// Forward declaration of `StringHolder` to properly resolve imports.
+namespace margelo::nitro::contacts { struct StringHolder; }
+
+#include
+#include
+#include
+#include "StringHolder.hpp"
+
+namespace margelo::nitro::contacts {
+
+ /**
+ * A struct which can be represented as a JavaScript object (Contact).
+ */
+ struct Contact {
+ public:
+ std::optional firstName SWIFT_PRIVATE;
+ std::optional lastName SWIFT_PRIVATE;
+ std::optional middleName SWIFT_PRIVATE;
+ std::optional> phoneNumbers SWIFT_PRIVATE;
+ std::optional> emailAddresses SWIFT_PRIVATE;
+ std::optional imageData SWIFT_PRIVATE;
+ std::optional thumbnailImageData SWIFT_PRIVATE;
+
+ public:
+ explicit Contact(std::optional firstName, std::optional lastName, std::optional middleName, std::optional> phoneNumbers, std::optional> emailAddresses, std::optional imageData, std::optional thumbnailImageData): firstName(firstName), lastName(lastName), middleName(middleName), phoneNumbers(phoneNumbers), emailAddresses(emailAddresses), imageData(imageData), thumbnailImageData(thumbnailImageData) {}
+ };
+
+} // namespace margelo::nitro::contacts
+
+namespace margelo::nitro {
+
+ using namespace margelo::nitro::contacts;
+
+ // C++ Contact <> JS Contact (object)
+ template <>
+ struct JSIConverter {
+ static inline Contact fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
+ jsi::Object obj = arg.asObject(runtime);
+ return Contact(
+ JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "firstName")),
+ JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "lastName")),
+ JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "middleName")),
+ JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, "phoneNumbers")),
+ JSIConverter>>::fromJSI(runtime, obj.getProperty(runtime, "emailAddresses")),
+ JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "imageData")),
+ JSIConverter>::fromJSI(runtime, obj.getProperty(runtime, "thumbnailImageData"))
+ );
+ }
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const Contact& arg) {
+ jsi::Object obj(runtime);
+ obj.setProperty(runtime, "firstName", JSIConverter>::toJSI(runtime, arg.firstName));
+ obj.setProperty(runtime, "lastName", JSIConverter>::toJSI(runtime, arg.lastName));
+ obj.setProperty(runtime, "middleName", JSIConverter>::toJSI(runtime, arg.middleName));
+ obj.setProperty(runtime, "phoneNumbers", JSIConverter>>::toJSI(runtime, arg.phoneNumbers));
+ obj.setProperty(runtime, "emailAddresses", JSIConverter>>::toJSI(runtime, arg.emailAddresses));
+ obj.setProperty(runtime, "imageData", JSIConverter>::toJSI(runtime, arg.imageData));
+ obj.setProperty(runtime, "thumbnailImageData", JSIConverter>::toJSI(runtime, arg.thumbnailImageData));
+ return obj;
+ }
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
+ if (!value.isObject()) {
+ return false;
+ }
+ jsi::Object obj = value.getObject(runtime);
+ if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "firstName"))) return false;
+ if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "lastName"))) return false;
+ if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "middleName"))) return false;
+ if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, "phoneNumbers"))) return false;
+ if (!JSIConverter>>::canConvert(runtime, obj.getProperty(runtime, "emailAddresses"))) return false;
+ if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "imageData"))) return false;
+ if (!JSIConverter>::canConvert(runtime, obj.getProperty(runtime, "thumbnailImageData"))) return false;
+ return true;
+ }
+ };
+
+} // namespace margelo::nitro
diff --git a/modules/ContactsNitroModule/nitrogen/generated/shared/c++/ContactFields.hpp b/modules/ContactsNitroModule/nitrogen/generated/shared/c++/ContactFields.hpp
new file mode 100644
index 000000000000..c3e8c115465e
--- /dev/null
+++ b/modules/ContactsNitroModule/nitrogen/generated/shared/c++/ContactFields.hpp
@@ -0,0 +1,102 @@
+///
+/// ContactFields.hpp
+/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
+/// https://github.com/mrousavy/nitro
+/// Copyright © 2024 Marc Rousavy @ Margelo
+///
+
+#pragma once
+
+#if __has_include()
+#include
+#else
+#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
+#endif
+#if __has_include()
+#include
+#else
+#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
+#endif
+#if __has_include()
+#include
+#else
+#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
+#endif
+
+namespace margelo::nitro::contacts {
+
+ /**
+ * An enum which can be represented as a JavaScript union (ContactFields).
+ */
+ enum class ContactFields {
+ FIRST_NAME SWIFT_NAME(firstName) = 0,
+ LAST_NAME SWIFT_NAME(lastName) = 1,
+ MIDDLE_NAME SWIFT_NAME(middleName) = 2,
+ PHONE_NUMBERS SWIFT_NAME(phoneNumbers) = 3,
+ EMAIL_ADDRESSES SWIFT_NAME(emailAddresses) = 4,
+ IMAGE_DATA SWIFT_NAME(imageData) = 5,
+ THUMBNAIL_IMAGE_DATA SWIFT_NAME(thumbnailImageData) = 6,
+ GIVEN_NAME_KEY SWIFT_NAME(givenNameKey) = 7,
+ } CLOSED_ENUM;
+
+} // namespace margelo::nitro::contacts
+
+namespace margelo::nitro {
+
+ using namespace margelo::nitro::contacts;
+
+ // C++ ContactFields <> JS ContactFields (union)
+ template <>
+ struct JSIConverter {
+ static inline ContactFields fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
+ std::string unionValue = JSIConverter::fromJSI(runtime, arg);
+ switch (hashString(unionValue.c_str(), unionValue.size())) {
+ case hashString("FIRST_NAME"): return ContactFields::FIRST_NAME;
+ case hashString("LAST_NAME"): return ContactFields::LAST_NAME;
+ case hashString("MIDDLE_NAME"): return ContactFields::MIDDLE_NAME;
+ case hashString("PHONE_NUMBERS"): return ContactFields::PHONE_NUMBERS;
+ case hashString("EMAIL_ADDRESSES"): return ContactFields::EMAIL_ADDRESSES;
+ case hashString("IMAGE_DATA"): return ContactFields::IMAGE_DATA;
+ case hashString("THUMBNAIL_IMAGE_DATA"): return ContactFields::THUMBNAIL_IMAGE_DATA;
+ case hashString("GIVEN_NAME_KEY"): return ContactFields::GIVEN_NAME_KEY;
+ default: [[unlikely]]
+ throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum ContactFields - invalid value!");
+ }
+ }
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, ContactFields arg) {
+ switch (arg) {
+ case ContactFields::FIRST_NAME: return JSIConverter::toJSI(runtime, "FIRST_NAME");
+ case ContactFields::LAST_NAME: return JSIConverter::toJSI(runtime, "LAST_NAME");
+ case ContactFields::MIDDLE_NAME: return JSIConverter::toJSI(runtime, "MIDDLE_NAME");
+ case ContactFields::PHONE_NUMBERS: return JSIConverter::toJSI(runtime, "PHONE_NUMBERS");
+ case ContactFields::EMAIL_ADDRESSES: return JSIConverter::toJSI(runtime, "EMAIL_ADDRESSES");
+ case ContactFields::IMAGE_DATA: return JSIConverter::toJSI(runtime, "IMAGE_DATA");
+ case ContactFields::THUMBNAIL_IMAGE_DATA: return JSIConverter::toJSI(runtime, "THUMBNAIL_IMAGE_DATA");
+ case ContactFields::GIVEN_NAME_KEY: return JSIConverter