react-native-litert-lm 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +270 -186
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +93 -37
  4. package/app.plugin.js +33 -0
  5. package/cpp/HybridLiteRTLM.cpp +571 -451
  6. package/cpp/HybridLiteRTLM.hpp +54 -23
  7. package/cpp/IOSDownloadHelper.h +24 -0
  8. package/cpp/cpp-adapter.cpp +2 -2
  9. package/cpp/include/litert_lm_engine.h +502 -0
  10. package/ios/IOSDownloadHelper.mm +129 -0
  11. package/ios/LiteRTLMAutolinking.mm +30 -0
  12. package/lib/hooks.d.ts +9 -4
  13. package/lib/hooks.js +34 -20
  14. package/lib/index.d.ts +1 -0
  15. package/lib/index.js +2 -5
  16. package/lib/memoryTracker.d.ts +1 -1
  17. package/lib/memoryTracker.js +1 -1
  18. package/lib/modelFactory.d.ts +11 -5
  19. package/lib/modelFactory.js +9 -4
  20. package/nitrogen/generated/android/LiteRTLMOnLoad.cpp +11 -4
  21. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.cpp +31 -37
  22. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.hpp +19 -22
  23. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec.kt +15 -18
  24. package/package.json +12 -5
  25. package/react-native-litert-lm.podspec +20 -7
  26. package/scripts/build-ios-engine.sh +283 -0
  27. package/scripts/download-ios-frameworks.sh +72 -0
  28. package/scripts/postinstall.js +116 -0
  29. package/scripts/stubs/cxx_bridge_stubs.cc +224 -0
  30. package/scripts/stubs/gemma_model_constraint_provider.cc +46 -0
  31. package/scripts/stubs/llguidance_stubs.c +101 -0
  32. package/src/hooks.ts +62 -39
  33. package/src/index.ts +4 -7
  34. package/src/memoryTracker.ts +1 -1
  35. package/src/modelFactory.ts +30 -5
@@ -18,34 +18,33 @@ namespace margelo::nitro::litertlm {
18
18
 
19
19
  using namespace facebook;
20
20
 
21
- class JHybridLiteRTLMSpec: public jni::HybridClass<JHybridLiteRTLMSpec, JHybridObject>,
22
- public virtual HybridLiteRTLMSpec {
21
+ class JHybridLiteRTLMSpec: public virtual HybridLiteRTLMSpec, public virtual JHybridObject {
23
22
  public:
24
- static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec;";
25
- static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
26
- static void registerNatives();
27
-
28
- protected:
29
- // C++ constructor (called from Java via `initHybrid()`)
30
- explicit JHybridLiteRTLMSpec(jni::alias_ref<jhybridobject> jThis) :
31
- HybridObject(HybridLiteRTLMSpec::TAG),
32
- HybridBase(jThis),
33
- _javaPart(jni::make_global(jThis)) {}
23
+ struct JavaPart: public jni::JavaClass<JavaPart, JHybridObject::JavaPart> {
24
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec;";
25
+ std::shared_ptr<JHybridLiteRTLMSpec> getJHybridLiteRTLMSpec();
26
+ };
27
+ struct CxxPart: public jni::HybridClass<CxxPart, JHybridObject::CxxPart> {
28
+ static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec$CxxPart;";
29
+ static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
30
+ static void registerNatives();
31
+ using HybridBase::HybridBase;
32
+ protected:
33
+ std::shared_ptr<JHybridObject> createHybridObject(const jni::local_ref<JHybridObject::JavaPart>& javaPart) override;
34
+ };
34
35
 
35
36
  public:
37
+ explicit JHybridLiteRTLMSpec(const jni::local_ref<JHybridLiteRTLMSpec::JavaPart>& javaPart):
38
+ HybridObject(HybridLiteRTLMSpec::TAG),
39
+ JHybridObject(javaPart),
40
+ _javaPart(jni::make_global(javaPart)) {}
36
41
  ~JHybridLiteRTLMSpec() override {
37
42
  // Hermes GC can destroy JS objects on a non-JNI Thread.
38
43
  jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); });
39
44
  }
40
45
 
41
46
  public:
42
- size_t getExternalMemorySize() noexcept override;
43
- bool equals(const std::shared_ptr<HybridObject>& other) override;
44
- void dispose() noexcept override;
45
- std::string toString() override;
46
-
47
- public:
48
- inline const jni::global_ref<JHybridLiteRTLMSpec::javaobject>& getJavaPart() const noexcept {
47
+ inline const jni::global_ref<JHybridLiteRTLMSpec::JavaPart>& getJavaPart() const noexcept {
49
48
  return _javaPart;
50
49
  }
51
50
 
@@ -70,9 +69,7 @@ namespace margelo::nitro::litertlm {
70
69
  void close() override;
71
70
 
72
71
  private:
73
- friend HybridBase;
74
- using HybridBase::HybridBase;
75
- jni::global_ref<JHybridLiteRTLMSpec::javaobject> _javaPart;
72
+ jni::global_ref<JHybridLiteRTLMSpec::JavaPart> _javaPart;
76
73
  };
77
74
 
78
75
  } // namespace margelo::nitro::litertlm
@@ -25,23 +25,6 @@ import com.margelo.nitro.core.HybridObject
25
25
  "LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
26
26
  )
27
27
  abstract class HybridLiteRTLMSpec: HybridObject() {
28
- @DoNotStrip
29
- private var mHybridData: HybridData = initHybrid()
30
-
31
- init {
32
- super.updateNative(mHybridData)
33
- }
34
-
35
- override fun updateNative(hybridData: HybridData) {
36
- mHybridData = hybridData
37
- super.updateNative(hybridData)
38
- }
39
-
40
- // Default implementation of `HybridObject.toString()`
41
- override fun toString(): String {
42
- return "[HybridObject LiteRTLM]"
43
- }
44
-
45
28
  // Properties
46
29
 
47
30
 
@@ -108,7 +91,21 @@ abstract class HybridLiteRTLMSpec: HybridObject() {
108
91
  @Keep
109
92
  abstract fun close(): Unit
110
93
 
111
- private external fun initHybrid(): HybridData
94
+ // Default implementation of `HybridObject.toString()`
95
+ override fun toString(): String {
96
+ return "[HybridObject LiteRTLM]"
97
+ }
98
+
99
+ // C++ backing class
100
+ @DoNotStrip
101
+ @Keep
102
+ protected open class CxxPart(javaPart: HybridLiteRTLMSpec): HybridObject.CxxPart(javaPart) {
103
+ // C++ JHybridLiteRTLMSpec::CxxPart::initHybrid(...)
104
+ external override fun initHybrid(): HybridData
105
+ }
106
+ override fun createCxxPart(): CxxPart {
107
+ return CxxPart(this)
108
+ }
112
109
 
113
110
  companion object {
114
111
  protected const val TAG = "HybridLiteRTLMSpec"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-litert-lm",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "High-performance LLM inference for React Native using LiteRT-LM. Optimized for Gemma 3n and other on-device language models.",
5
5
  "license": "MIT",
6
6
  "author": "Hugh Chen (https://github.com/hung-yueh)",
@@ -37,9 +37,14 @@
37
37
  "android/src",
38
38
  "android/build.gradle",
39
39
  "android/CMakeLists.txt",
40
- "ios",
40
+ "ios/*.mm",
41
+ "ios/*.m",
41
42
  "cpp",
42
43
  "nitrogen/generated",
44
+ "scripts/postinstall.js",
45
+ "scripts/download-ios-frameworks.sh",
46
+ "scripts/build-ios-engine.sh",
47
+ "scripts/stubs",
43
48
  "react-native.config.js",
44
49
  "react-native-litert-lm.podspec",
45
50
  "app.plugin.js",
@@ -50,12 +55,14 @@
50
55
  "node": ">=22"
51
56
  },
52
57
  "scripts": {
58
+ "postinstall": "node scripts/postinstall.js",
53
59
  "build": "tsc",
54
60
  "typecheck": "tsc --noEmit",
55
61
  "lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
56
62
  "prepack": "npm run build",
57
63
  "specs": "npx nitrogen",
58
- "clean": "rm -rf lib android/build ios/build nitrogen/generated",
64
+ "clean": "rm -rf lib android/build ios/build ios/Frameworks nitrogen/generated",
65
+ "download-frameworks": "scripts/download-ios-frameworks.sh",
59
66
  "android": "expo run:android",
60
67
  "android:clean": "cd android && ./gradlew clean",
61
68
  "ios": "expo run:ios",
@@ -65,7 +72,7 @@
65
72
  "@expo/config-plugins": "~54.0.4",
66
73
  "@types/react": "~19.1.10",
67
74
  "expo": "^54.0.31",
68
- "nitrogen": "^0.34.1",
75
+ "nitrogen": "^0.35.0",
69
76
  "react": "19.1.0",
70
77
  "react-native": "0.81.5",
71
78
  "release-it": "^19.2.4",
@@ -82,6 +89,6 @@
82
89
  }
83
90
  },
84
91
  "dependencies": {
85
- "react-native-nitro-modules": "^0.34.1"
92
+ "react-native-nitro-modules": "^0.35.0"
86
93
  }
87
94
  }
@@ -16,36 +16,49 @@ Pod::Spec.new do |s|
16
16
 
17
17
  s.source_files = [
18
18
  # Implementation (C++)
19
- "cpp/**/*.{hpp,cpp}",
19
+ "cpp/**/*.{hpp,cpp,h}",
20
20
  # Autolinking (Objective-C++)
21
21
  "ios/**/*.{m,mm}",
22
22
  # Nitrogen generated iOS bridge
23
23
  "nitrogen/generated/ios/**/*.{mm,swift}",
24
24
  ]
25
25
 
26
+ # Exclude Android-only JNI files from iOS build
27
+ s.exclude_files = [
28
+ "cpp/cpp-adapter.cpp",
29
+ ]
30
+
31
+ # Prebuilt LiteRT-LM C engine (static library built from Bazel //c:engine target).
32
+ # Downloaded from GitHub releases by postinstall.js, or built locally via:
33
+ # scripts/build-ios-engine.sh
34
+ s.vendored_frameworks = 'ios/Frameworks/LiteRTLM.xcframework'
35
+
26
36
  s.pod_target_xcconfig = {
27
37
  'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20',
28
38
  'CLANG_CXX_LIBRARY' => 'libc++',
29
39
  'HEADER_SEARCH_PATHS' => [
30
40
  '"$(PODS_TARGET_SRCROOT)/cpp"',
41
+ '"$(PODS_TARGET_SRCROOT)/cpp/include"',
31
42
  '"$(PODS_TARGET_SRCROOT)/nitrogen/generated/shared/c++"',
32
43
  '"$(PODS_TARGET_SRCROOT)/nitrogen/generated/ios"',
33
44
  ].join(' '),
34
- # Stub mode - LiteRT-LM iOS not yet available
35
- 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) LITERT_LM_IOS_STUB=1',
45
+ 'OTHER_LDFLAGS' => '$(inherited) -ObjC',
36
46
  }
37
47
 
38
48
  # Load nitrogen autolinking
39
49
  load 'nitrogen/generated/ios/LiteRTLM+autolinking.rb'
40
50
  add_nitrogen_files(s)
41
51
 
42
- # Core dependencies only - no LLM runtime yet
52
+ # Core React Native dependencies
43
53
  s.dependency 'React-jsi'
44
54
  s.dependency 'React-callinvoker'
45
55
  s.dependency 'ReactCommon/turbomodule/core'
46
-
47
- # TODO: Add LiteRT-LM iOS dependency when officially released
48
- # s.dependency 'LiteRTLM', '~> 1.0'
56
+
57
+ # Apple frameworks needed by LiteRT-LM engine
58
+ # Metal/MPS: GPU inference, Accelerate: BLAS/LAPACK, CoreML: delegate
59
+ s.frameworks = ['Metal', 'MetalPerformanceShaders', 'Accelerate', 'CoreML', 'CoreGraphics']
60
+ s.libraries = ['c++']
49
61
 
50
62
  install_modules_dependencies(s)
51
63
  end
64
+
@@ -0,0 +1,283 @@
1
+ #!/bin/bash
2
+ # build-ios-engine.sh
3
+ #
4
+ # Builds the LiteRT-LM C engine as a static library for iOS (device + simulator)
5
+ # using Bazel, then packages it into an XCFramework for CocoaPods.
6
+ #
7
+ # Prerequisites:
8
+ # - Bazel 7.6.1+ (via Bazelisk recommended)
9
+ # - Xcode command line tools
10
+ #
11
+ # Usage:
12
+ # ./scripts/build-ios-engine.sh
13
+ #
14
+ # Output:
15
+ # ios/Frameworks/LiteRTLM.xcframework/ (static library + headers)
16
+
17
+ set -euo pipefail
18
+
19
+ LITERT_LM_VERSION="v0.9.0"
20
+ LITERT_LM_REPO="https://github.com/google-ai-edge/LiteRT-LM.git"
21
+ FRAMEWORK_NAME="LiteRTLM"
22
+
23
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
24
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
25
+ OUTPUT_DIR="$PROJECT_ROOT/ios/Frameworks"
26
+ C_API_HEADER_DIR="$PROJECT_ROOT/cpp/include"
27
+ BUILD_DIR="$PROJECT_ROOT/.litert-lm-build"
28
+
29
+ echo "==> Building LiteRT-LM ${LITERT_LM_VERSION} C engine for iOS..."
30
+ echo ""
31
+
32
+ # ---- 1. Clone / update the LiteRT-LM repo --------------------------------
33
+ echo "==> Step 1: Preparing LiteRT-LM source..."
34
+ if [ -f "$BUILD_DIR/LiteRT-LM/.bazelrc" ] && [ -f "$BUILD_DIR/LiteRT-LM/requirements.txt" ]; then
35
+ echo " Source already exists, checking out ${LITERT_LM_VERSION}..."
36
+ cd "$BUILD_DIR/LiteRT-LM"
37
+ git fetch --tags 2>/dev/null || true
38
+ git checkout "$LITERT_LM_VERSION" 2>/dev/null || git checkout "tags/$LITERT_LM_VERSION"
39
+ else
40
+ # Clean up any failed previous clone
41
+ rm -rf "$BUILD_DIR/LiteRT-LM"
42
+ mkdir -p "$BUILD_DIR"
43
+ echo " Cloning LiteRT-LM (shallow, skipping LFS)..."
44
+
45
+ # Clone without LFS filter to avoid requiring git-lfs installation.
46
+ # The prebuilt/ directory will contain LFS pointer files but we don't
47
+ # need those — we're building the engine from source.
48
+ GIT_LFS_SKIP_SMUDGE=1 git clone --depth 1 --branch "$LITERT_LM_VERSION" \
49
+ -c filter.lfs.smudge=cat \
50
+ -c filter.lfs.process= \
51
+ -c filter.lfs.clean=cat \
52
+ -c filter.lfs.required=false \
53
+ "$LITERT_LM_REPO" "$BUILD_DIR/LiteRT-LM"
54
+
55
+ cd "$BUILD_DIR/LiteRT-LM"
56
+ fi
57
+
58
+ LITERT_SRC="$BUILD_DIR/LiteRT-LM"
59
+
60
+ # ---- 2. Verify Bazel is available -----------------------------------------
61
+ echo ""
62
+ echo "==> Step 2: Checking Bazel..."
63
+ if command -v bazelisk &>/dev/null; then
64
+ BAZEL="bazelisk"
65
+ elif command -v bazel &>/dev/null; then
66
+ BAZEL="bazel"
67
+ else
68
+ echo "Error: Bazel is not installed."
69
+ echo "Install via: brew install bazelisk"
70
+ echo "Or download from: https://github.com/bazelbuild/bazelisk"
71
+ exit 1
72
+ fi
73
+
74
+ BAZEL_VERSION=$($BAZEL --version 2>&1 | head -1)
75
+ echo " Using: $BAZEL ($BAZEL_VERSION)"
76
+
77
+ # ---- 3. Build the C engine static library for iOS -------------------------
78
+ echo ""
79
+ echo "==> Step 3: Building //c:engine for iOS..."
80
+
81
+ cd "$LITERT_SRC"
82
+
83
+ # Get Bazel output base (where all .o files live in the actual filesystem)
84
+ BAZEL_OUTPUT_BASE=$($BAZEL info output_base 2>/dev/null)
85
+ BAZEL_EXECROOT="$BAZEL_OUTPUT_BASE/execroot"
86
+
87
+ STAGE_DIR="$BUILD_DIR/staged-libs"
88
+ mkdir -p "$STAGE_DIR"
89
+
90
+ # Helper: build for a config, then merge ALL transitive .o files into one .a
91
+ # Bazel's cc_library produces thin archives — the engine's transitive deps
92
+ # (absl, protobuf, runtime, KleidiAI, etc.) are separate .o files that must
93
+ # be merged into a single self-contained static library for Xcode.
94
+ build_fat_static_lib() {
95
+ local CONFIG="$1"
96
+ local CONFIG_DIR="$2" # e.g. "ios_arm64-opt" or "ios_sim_arm64-opt"
97
+ local OUTPUT_PATH="$3"
98
+
99
+ echo " Building for $CONFIG..."
100
+ # Build both the engine AND all cc_proto_library targets in a single Bazel
101
+ # invocation. Bazel's cc_proto_library compiles proto-generated code, but
102
+ # the .pb.o files only appear in the output tree if these targets are
103
+ # explicitly requested alongside the engine.
104
+ $BAZEL build \
105
+ //c:engine \
106
+ @sentencepiece//:sentencepiece_cc_proto \
107
+ @sentencepiece//:sentencepiece_model_cc_proto \
108
+ //runtime/proto:engine_cc_proto \
109
+ //runtime/proto:sampler_params_cc_proto \
110
+ //runtime/proto:llm_metadata_cc_proto \
111
+ //runtime/proto:token_cc_proto \
112
+ //runtime/proto:llm_model_type_cc_proto \
113
+ //runtime/util:external_file_cc_proto \
114
+ @com_google_protobuf//:protobuf \
115
+ @com_googlesource_code_re2//:re2 \
116
+ --config=$CONFIG 2>&1 | tail -5
117
+
118
+ echo " Collecting transitive object files from $CONFIG_DIR..."
119
+ local OBJ_LIST="$STAGE_DIR/${CONFIG}-objects.txt"
120
+ find "$BAZEL_EXECROOT" -path "*/${CONFIG_DIR}/bin/*" -name "*.o" \
121
+ ! -name "*.h.processed" 2>/dev/null | sort > "$OBJ_LIST"
122
+
123
+ # ---- Compile stubs for Rust/llguidance deps (unavailable on iOS) ----------
124
+ local EXTRA_OBJS="$STAGE_DIR/${CONFIG}-extra-objs"
125
+ rm -rf "$EXTRA_OBJS"
126
+ mkdir -p "$EXTRA_OBJS"
127
+
128
+ local STUBS_DIR="$PROJECT_ROOT/scripts/stubs"
129
+ local STUB_FILES=$(find "$STUBS_DIR" \( -name "*.cc" -o -name "*.c" \) 2>/dev/null)
130
+ if [ -n "$STUB_FILES" ]; then
131
+ echo " Compiling stubs for unavailable dependencies..."
132
+ local SDK_NAME="iphoneos"
133
+ local TARGET_TRIPLE="arm64-apple-ios15.0"
134
+ if [[ "$CONFIG_DIR" == *"sim"* ]]; then
135
+ SDK_NAME="iphonesimulator"
136
+ TARGET_TRIPLE="arm64-apple-ios15.0-simulator"
137
+ fi
138
+ local SDK_PATH=$(xcrun --sdk "$SDK_NAME" --show-sdk-path)
139
+
140
+ for STUB_SRC in $STUB_FILES; do
141
+ local STUB_BASE=$(basename "$STUB_SRC")
142
+ local STUB_NAME="${STUB_BASE%.*}"
143
+ local STUB_EXT="${STUB_BASE##*.}"
144
+ echo " → $STUB_NAME ($STUB_EXT)"
145
+
146
+ if [ "$STUB_EXT" = "cc" ]; then
147
+ xcrun clang++ -c -std=c++20 \
148
+ -target "$TARGET_TRIPLE" \
149
+ -isysroot "$SDK_PATH" \
150
+ -DNDEBUG \
151
+ -o "$EXTRA_OBJS/${STUB_NAME}.o" \
152
+ "$STUB_SRC" 2>&1 || true
153
+ else
154
+ xcrun clang -c \
155
+ -target "$TARGET_TRIPLE" \
156
+ -isysroot "$SDK_PATH" \
157
+ -DNDEBUG \
158
+ -o "$EXTRA_OBJS/${STUB_NAME}.o" \
159
+ "$STUB_SRC" 2>&1 || true
160
+ fi
161
+ done
162
+
163
+ # Add successfully compiled stubs to the object list
164
+ find "$EXTRA_OBJS" -name "*.o" -size +0c >> "$OBJ_LIST"
165
+ fi
166
+
167
+ local OBJ_COUNT=$(wc -l < "$OBJ_LIST" | tr -d ' ')
168
+ echo " Found $OBJ_COUNT total object files (including proto + stubs)"
169
+
170
+ if [ "$OBJ_COUNT" -eq 0 ]; then
171
+ echo "Error: No object files found for $CONFIG in $CONFIG_DIR"
172
+ exit 1
173
+ fi
174
+
175
+ # Merge all .o files into a single fat static library using libtool
176
+ echo " Merging into fat static library..."
177
+ xcrun libtool -static -o "$OUTPUT_PATH" -filelist "$OBJ_LIST" 2>&1 | grep -v "has no symbols" || true
178
+
179
+ local LIB_SIZE=$(du -h "$OUTPUT_PATH" | cut -f1)
180
+ echo " ✅ $CONFIG: $LIB_SIZE ($OBJ_COUNT objects)"
181
+ }
182
+
183
+ # Build for device (arm64)
184
+ DEVICE_LIB="$STAGE_DIR/libengine-device.a"
185
+ build_fat_static_lib "ios_arm64" "ios_arm64-opt" "$DEVICE_LIB"
186
+
187
+ echo ""
188
+
189
+ # Build for simulator (sim_arm64)
190
+ SIM_LIB="$STAGE_DIR/libengine-sim.a"
191
+ build_fat_static_lib "ios_sim_arm64" "ios_sim_arm64-opt" "$SIM_LIB"
192
+
193
+ echo ""
194
+ echo " Device lib: $DEVICE_LIB ($(du -h "$DEVICE_LIB" | cut -f1))"
195
+ echo " Simulator lib: $SIM_LIB ($(du -h "$SIM_LIB" | cut -f1))"
196
+
197
+ # ---- 4. Copy the C API header ---------------------------------------------
198
+ echo ""
199
+ echo "==> Step 4: Vendoring C API header..."
200
+ mkdir -p "$C_API_HEADER_DIR"
201
+ cp "$LITERT_SRC/c/engine.h" "$C_API_HEADER_DIR/litert_lm_engine.h"
202
+ echo " ✅ Copied engine.h → cpp/include/litert_lm_engine.h"
203
+
204
+ # ---- 5. Create XCFramework from static libraries --------------------------
205
+ echo ""
206
+ echo "==> Step 5: Creating XCFramework..."
207
+
208
+ rm -rf "$OUTPUT_DIR"
209
+ mkdir -p "$OUTPUT_DIR"
210
+
211
+ TMP_DIR="$(mktemp -d)"
212
+ cleanup() { rm -rf "$TMP_DIR"; }
213
+ trap cleanup EXIT
214
+
215
+ # Create framework bundles from static libraries
216
+ for ARCH_NAME in "device" "simulator"; do
217
+ if [ "$ARCH_NAME" = "device" ]; then
218
+ LIB_PATH="$DEVICE_LIB"
219
+ else
220
+ LIB_PATH="$SIM_LIB"
221
+ fi
222
+
223
+ FW_DIR="$TMP_DIR/$ARCH_NAME/$FRAMEWORK_NAME.framework"
224
+ mkdir -p "$FW_DIR/Headers"
225
+
226
+ # Copy static lib as the framework binary
227
+ cp "$LIB_PATH" "$FW_DIR/$FRAMEWORK_NAME"
228
+
229
+ # Copy headers
230
+ cp "$C_API_HEADER_DIR/litert_lm_engine.h" "$FW_DIR/Headers/"
231
+
232
+ # Create Info.plist
233
+ cat > "$FW_DIR/Info.plist" << PLIST
234
+ <?xml version="1.0" encoding="UTF-8"?>
235
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
236
+ <plist version="1.0">
237
+ <dict>
238
+ <key>CFBundleDevelopmentRegion</key>
239
+ <string>en</string>
240
+ <key>CFBundleExecutable</key>
241
+ <string>${FRAMEWORK_NAME}</string>
242
+ <key>CFBundleIdentifier</key>
243
+ <string>com.google.ai.edge.litert-lm</string>
244
+ <key>CFBundleInfoDictionaryVersion</key>
245
+ <string>6.0</string>
246
+ <key>CFBundleName</key>
247
+ <string>${FRAMEWORK_NAME}</string>
248
+ <key>CFBundlePackageType</key>
249
+ <string>FMWK</string>
250
+ <key>CFBundleShortVersionString</key>
251
+ <string>0.9.0</string>
252
+ <key>CFBundleVersion</key>
253
+ <string>1</string>
254
+ <key>MinimumOSVersion</key>
255
+ <string>15.0</string>
256
+ </dict>
257
+ </plist>
258
+ PLIST
259
+ done
260
+
261
+ # Create XCFramework
262
+ xcodebuild -create-xcframework \
263
+ -framework "$TMP_DIR/device/$FRAMEWORK_NAME.framework" \
264
+ -framework "$TMP_DIR/simulator/$FRAMEWORK_NAME.framework" \
265
+ -output "$OUTPUT_DIR/$FRAMEWORK_NAME.xcframework" 2>&1
266
+
267
+ echo " ✅ XCFramework created at: ios/Frameworks/${FRAMEWORK_NAME}.xcframework"
268
+
269
+ # ---- 6. Create zip for release asset --------------------------------------
270
+ echo ""
271
+ echo "==> Step 6: Creating release asset zip..."
272
+ cd "$OUTPUT_DIR"
273
+ zip -r "$PROJECT_ROOT/LiteRTLM-ios-frameworks.zip" . -x ".*" 2>&1
274
+ ZIP_SIZE=$(du -h "$PROJECT_ROOT/LiteRTLM-ios-frameworks.zip" | cut -f1)
275
+ echo " ✅ Created LiteRTLM-ios-frameworks.zip (${ZIP_SIZE})"
276
+
277
+ echo ""
278
+ echo "==> Done! iOS engine built and packaged."
279
+ echo ""
280
+ echo "Contents:"
281
+ find "$OUTPUT_DIR" -type f | head -20 | while read f; do
282
+ echo " $(echo "$f" | sed "s|$PROJECT_ROOT/||") ($(du -h "$f" | cut -f1))"
283
+ done
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # download-ios-frameworks.sh
3
+ #
4
+ # Downloads prebuilt LiteRT-LM iOS static engine from this project's GitHub
5
+ # releases. If the prebuilt asset is not available, falls back to building
6
+ # from source via Bazel (see build-ios-engine.sh).
7
+ #
8
+ # The XCFramework contains a static library compiled from the LiteRT-LM
9
+ # C engine (//c:engine Bazel target) for both device (arm64) and simulator
10
+ # (sim_arm64).
11
+ #
12
+ # Usage:
13
+ # ./scripts/download-ios-frameworks.sh
14
+
15
+ set -euo pipefail
16
+
17
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
18
+ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
19
+ OUTPUT_DIR="$PROJECT_ROOT/ios/Frameworks"
20
+ C_API_HEADER_DIR="$PROJECT_ROOT/cpp/include"
21
+
22
+ LITERT_LM_VERSION="v0.9.0"
23
+ GITHUB_RAW="https://github.com/google-ai-edge/LiteRT-LM/raw/${LITERT_LM_VERSION}"
24
+
25
+ # Read version from package.json
26
+ PACKAGE_VERSION=$(node -e "console.log(require('$PROJECT_ROOT/package.json').version)" 2>/dev/null || echo "0.0.0")
27
+ GITHUB_REPO="hung-yueh/react-native-litert-lm"
28
+ ASSET_NAME="LiteRTLM-ios-frameworks.zip"
29
+
30
+ # Skip if already present
31
+ if [ -d "$OUTPUT_DIR" ] && [ "$(find "$OUTPUT_DIR" -name "*.xcframework" 2>/dev/null | wc -l)" -gt 0 ]; then
32
+ echo "[LiteRT-LM] iOS frameworks already present at ios/Frameworks/, skipping."
33
+ exit 0
34
+ fi
35
+
36
+ # ---- Ensure C API header is vendored --------------------------------------
37
+ echo "[LiteRT-LM] Vendoring C API header..."
38
+ mkdir -p "$C_API_HEADER_DIR"
39
+ curl -fsSL -o "$C_API_HEADER_DIR/litert_lm_engine.h" \
40
+ "${GITHUB_RAW}/c/engine.h" 2>/dev/null || true
41
+
42
+ # ---- Try downloading prebuilt from our GitHub releases --------------------
43
+ RELEASE_URL="https://github.com/${GITHUB_REPO}/releases/download/v${PACKAGE_VERSION}/${ASSET_NAME}"
44
+
45
+ echo "[LiteRT-LM] Attempting to download prebuilt iOS engine from:"
46
+ echo " ${RELEASE_URL}"
47
+
48
+ TMP_ZIP="$PROJECT_ROOT/.ios-frameworks-tmp.zip"
49
+ if curl -fsSL -o "$TMP_ZIP" "$RELEASE_URL" 2>/dev/null; then
50
+ echo "[LiteRT-LM] Download successful, extracting..."
51
+ rm -rf "$OUTPUT_DIR"
52
+ mkdir -p "$OUTPUT_DIR"
53
+ unzip -o -q "$TMP_ZIP" -d "$OUTPUT_DIR"
54
+ rm -f "$TMP_ZIP"
55
+ echo "[LiteRT-LM] ✅ iOS frameworks installed from prebuilt release."
56
+ exit 0
57
+ fi
58
+
59
+ rm -f "$TMP_ZIP"
60
+ echo "[LiteRT-LM] Prebuilt not available for v${PACKAGE_VERSION}."
61
+
62
+ # ---- Fall back to building from source ------------------------------------
63
+ echo "[LiteRT-LM] Falling back to building from source via Bazel..."
64
+ echo ""
65
+
66
+ if [ -x "$SCRIPT_DIR/build-ios-engine.sh" ]; then
67
+ exec "$SCRIPT_DIR/build-ios-engine.sh"
68
+ else
69
+ echo "Error: build-ios-engine.sh not found or not executable."
70
+ echo "Run manually: ./scripts/build-ios-engine.sh"
71
+ exit 1
72
+ fi