From harness-claude
Guides building custom native modules for React Native using Expo Modules API (Swift/Kotlin) and Turbo Modules. Covers scaffolding, defining interfaces, and calling native code from JS.
How this skill is triggered — by the user, by Claude, or both
Slash command
/harness-claude:mobile-native-modulesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Bridge native platform APIs into React Native with Expo Modules API and Turbo Modules
Bridge native platform APIs into React Native with Expo Modules API and Turbo Modules
Prefer existing Expo SDK modules and community packages first. Most common native functionality (camera, location, notifications, file system, biometrics) already has a well-maintained package. Only write custom native modules for genuinely missing functionality.
Use Expo Modules API for new native modules in Expo projects. It provides a unified Swift/Kotlin API that is simpler than React Native's Turbo Modules.
npx create-expo-module my-native-module
This scaffolds a module with iOS (Swift) and Android (Kotlin) source files.
// ios/MyNativeModule.swift
import ExpoModulesCore
public class MyNativeModule: Module {
public func definition() -> ModuleDefinition {
Name("MyNativeModule")
// Synchronous function
Function("getDeviceId") { () -> String in
return UIDevice.current.identifierForVendor?.uuidString ?? ""
}
// Async function
AsyncFunction("fetchHealthData") { (type: String, promise: Promise) in
HealthKitManager.fetch(type: type) { result in
switch result {
case .success(let data):
promise.resolve(data)
case .failure(let error):
promise.reject(error)
}
}
}
// Events
Events("onStatusChange")
// View component
View(MyNativeView.self) {
Prop("color") { (view, color: UIColor) in
view.backgroundColor = color
}
}
}
}
// android/src/main/java/expo/modules/mynativemodule/MyNativeModule.kt
package expo.modules.mynativemodule
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class MyNativeModule : Module() {
override fun definition() = ModuleDefinition {
Name("MyNativeModule")
Function("getDeviceId") {
android.provider.Settings.Secure.getString(
appContext.reactContext?.contentResolver,
android.provider.Settings.Secure.ANDROID_ID
)
}
AsyncFunction("fetchHealthData") { type: String ->
// Android Health Connect implementation
}
Events("onStatusChange")
}
}
// src/MyNativeModule.ts
import { requireNativeModule } from 'expo-modules-core';
const MyNativeModule = requireNativeModule('MyNativeModule');
export function getDeviceId(): string {
return MyNativeModule.getDeviceId();
}
export async function fetchHealthData(type: string): Promise<HealthData> {
return MyNativeModule.fetchHealthData(type);
}
// Listen to events
import { EventEmitter } from 'expo-modules-core';
const emitter = new EventEmitter(MyNativeModule);
export function onStatusChange(callback: (status: string) => void) {
return emitter.addListener('onStatusChange', callback);
}
.ios.ts and .android.ts extensions.src/
biometrics.ts # Shared interface
biometrics.ios.ts # iOS implementation (Face ID)
biometrics.android.ts # Android implementation (Fingerprint)
// plugin/withMyModule.ts
import { ConfigPlugin, withInfoPlist, withAndroidManifest } from 'expo/config-plugins';
const withMyModule: ConfigPlugin = (config) => {
config = withInfoPlist(config, (config) => {
config.modResults.NSHealthShareUsageDescription = 'Access health data';
return config;
});
config = withAndroidManifest(config, (config) => {
// Add Android permissions
return config;
});
return config;
};
export default withMyModule;
Expo Modules API vs. Turbo Modules: Expo Modules API provides a higher-level DSL in Swift/Kotlin with automatic type conversion, event support, and view definitions. Turbo Modules (React Native's new architecture) use C++ and codegen for type-safe bridging. Use Expo Modules for Expo projects; use Turbo Modules for bare RN projects or when you need C++ performance.
Threading: By default, native module functions run on the main thread (iOS) or the native modules thread (Android). For heavy computation, dispatch to a background queue/thread and use promises to return results.
Data types that cross the bridge: Strings, numbers, booleans, arrays, and dictionaries (objects) are automatically converted. For complex types, serialize to JSON. Binary data should use base64 encoding or file URIs.
Common mistakes:
https://docs.expo.dev/modules/overview/
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeGuides building and integrating native modules in React Native, covering Turbo Modules, Swift/iOS and Kotlin/Android bridging, and platform APIs.
Guides creation of React Native native modules using Expo Modules API, Turbo Modules, or Fabric Components with TypeScript APIs, Swift iOS, and Kotlin Android implementations.
Creates and modifies Expo native modules and views using the Expo Modules API (Swift, Kotlin, TypeScript). Covers module definition DSL, native views, shared objects, config plugins, lifecycle hooks, autolinking, and type system.