π» Running Guide
π§ How to Run an ONNX Model in a Swift iOS Project (Xcode)
π What is this?
This guide walks you through integrating and running an ONNX model using onnxruntime-objc in an iOS app with Swift.
β Prerequisites
π¦ Required Software
- Xcode installed
- Homebrew installed
- Ruby β₯ 3.1
- CocoaPods
- ONNX model files:
model.onnx
vocab.json
label_map.json
Install Homebrew if not installed:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Install Ruby and CocoaPods:
brew install ruby
gem install cocoapods
β 1. Create and Setup the Xcode Project
π Project Setup
cd ~
mkdir ONNXruntime
cd ONNXruntime
xcode-select --install # if not installed
xcodebuild -project . # just to initialize
Then in Xcode:
- File β New β Project β iOS β App
- Name it: ONNXruntime
- Save inside your ~/ONNXruntime folder
β 2. Place Resources
π Add Model Files
Put these files in your project folder:
model.onnx
vocab.json
label_map.json
Make sure to:
- Drag them into Xcode's Navigator
- Set Target Membership β
- Check "Copy if needed"
β 3. Initialize and Install CocoaPods
π¦ CocoaPods Setup
In terminal:
cd ~/ONNXruntime
pod init
Edit the Podfile and add:
platform :ios, '13.0'
target 'ONNXruntime' do
use_frameworks!
pod 'onnxruntime-objc'
end
Then install pods:
pod install
Important: Now always open the project using ONNXruntime.xcworkspace
β 4. Add Swift Model Logic
Create a new file: Utils.swift and paste this code:
import Foundation
import onnxruntime_objc
class Tokenizer {
private var vocab: [String: Int]
init(vocab: [String: Int]) {
self.vocab = vocab
}
func tokenize(text: String) -> [Int] {
let words = text.lowercased().split(separator: " ").map { String($0) }
return words.map { vocab[$0] ?? vocab["<OOV>"] ?? 1 }
}
}
class LabelVocabLoader {
private(set) var labelMap: [Int: String] = [:]
private(set) var vocab: [String: Int] = [:]
init(labelMapPath: String, vocabPath: String) throws {
let labelMapData = try Data(contentsOf: URL(fileURLWithPath: labelMapPath))
let vocabData = try Data(contentsOf: URL(fileURLWithPath: vocabPath))
if let labelMapJson = try JSONSerialization.jsonObject(with: labelMapData) as? [String: String] {
for (key, value) in labelMapJson {
if let intKey = Int(key) {
labelMap[intKey] = value
}
}
}
if let vocabJson = try JSONSerialization.jsonObject(with: vocabData) as? [String: Int] {
vocab = vocabJson
}
}
}
class ONNXModelRunner {
private var session: ORTSession
private var labelMap: [Int: String]
private var vocab: [String: Int]
private let maxLen = 30
init() throws {
guard let modelPath = Bundle.main.path(forResource: "model", ofType: "onnx"),
let labelPath = Bundle.main.path(forResource: "label_map", ofType: "json"),
let vocabPath = Bundle.main.path(forResource: "vocab", ofType: "json") else {
throw NSError(domain: "Paths", code: 1, userInfo: [NSLocalizedDescriptionKey: "Missing model or json files"])
}
let loader = try LabelVocabLoader(labelMapPath: labelPath, vocabPath: vocabPath)
self.labelMap = loader.labelMap
self.vocab = loader.vocab
let env = try ORTEnv(loggingLevel: .warning)
let sessionOptions = try ORTSessionOptions()
self.session = try ORTSession(env: env, modelPath: modelPath, sessionOptions: sessionOptions)
}
func predict(text: String) throws -> [(String, Float)] {
let tokenizer = Tokenizer(vocab: vocab)
var tokens = tokenizer.tokenize(text: text)
if tokens.count < maxLen {
tokens += Array(repeating: 0, count: maxLen - tokens.count)
} else if tokens.count > maxLen {
tokens = Array(tokens.prefix(maxLen))
}
let int32Tokens = tokens.map { Int32($0) }
let inputData = Data(from: int32Tokens)
let tensor = try ORTValue(
tensorData: NSMutableData(data: inputData),
elementType: .int32,
shape: [1, NSNumber(value: maxLen)]
)
let outputs = try session.run(
withInputs: ["input": tensor],
outputNames: Set(["sequential"]),
runOptions: nil
)
guard let result = outputs["sequential"]?.value as? [[Float]] else {
throw NSError(domain: "ONNX", code: 2, userInfo: [NSLocalizedDescriptionKey: "No valid output"])
}
return result[0].enumerated().map { (i, score) in
(labelMap[i] ?? "Unknown", score)
}
}
}
private extension Data {
init<T>(from array: [T]) {
self = array.withUnsafeBufferPointer { buffer in
Data(buffer: buffer)
}
}
}
β 5. Update ContentView.swift
Replace ContentView.swift with this SwiftUI implementation:
import SwiftUI
struct ContentView: View {
@State private var result = "Processing..."
var body: some View {
VStack(spacing: 20) {
Text(result)
.padding()
Button("Run Inference") {
do {
let runner = try ONNXModelRunner()
let predictions = try runner.predict(text: "I hate this product")
result = predictions
.map { "\($0.0): \($0.1)" }
.joined(separator: "\n")
} catch {
result = "Error: \(error.localizedDescription)"
}
}
}
.padding()
}
}
β 6. Build and Run
π Final Steps
- Open ONNXruntime.xcworkspace
- Select a simulator or your connected iPhone
- Press Run βΆοΈ
β 7. Expected Output
Hate: 0.9732
Not_hate: 0.0123
Neutral: 0.0145
β 8. Troubleshooting
β οΈ Common Issues
- Build Errors: Make sure you're using .xcworkspace, not .xcodeproj
- Missing Files: Verify all model files are in the correct location and added to target
- Memory Issues: Check model size and optimize if needed
- Performance: Consider using background threads for inference
β 9. iOS Demo
iOS Demo
π¬ ONNX model working in an iOS app (Swift + CoreML)

π§ Full Guide: How to Run an ONNX Model on Android (Java)
π What is this?
This guide walks you through creating and deploying a fully offline ONNX-based text classification app on Android using Java.
β 1. Project Setup
π Create a new Android project
- Use Java as the programming language
- Use Empty Activity
β 2. Prepare Assets
π¦ Setup Project Structure
Inside your Android project, create this folder:
app/src/main/assets/
Put these files inside:
model.onnx
β your exported ONNX modelvocab.json
β dictionary mapping words to token IDslabelMap.json
β mapping prediction indexes to labels
π Example Files
Example of vocab.json:
{
"<OOV>": 1,
"hello": 2,
"world": 3
}
Example of labelMap.json:
{
"0": "Not_Hate",
"1": "Hate"
}
β 3. Add ONNX Runtime
βοΈ Configure Dependencies
In your app/build.gradle, add the ONNX Runtime for Android:
dependencies {
implementation 'ai.onnxruntime:onnxruntime-android:1.16.3'
}
Then click Sync Now.
β 4. Java Implementation
Replace the contents of MainActivity.java with this code:
package com.example.hateclassifier;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import ai.onnxruntime.*;
import org.json.JSONObject;
import java.io.*;
import java.util.*;
public class MainActivity extends AppCompatActivity {
private OrtEnvironment env;
private OrtSession session;
private Map<Integer, String> labelMap;
private Map<String, Integer> vocab;
private static final int MAX_LEN = 30;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText inputText = findViewById(R.id.inputText);
Button sendButton = findViewById(R.id.sendButton);
TextView chatOutput = findViewById(R.id.chatOutput);
try {
LabelVocabLoader loader = new LabelVocabLoader("labelMap.json", "vocab.json");
labelMap = loader.getLabelMap();
vocab = loader.getVocab();
Log.d("ONNX", "Loading model...");
session = loadModel("model.onnx");
env = OrtEnvironment.getEnvironment();
} catch (Exception e) {
e.printStackTrace();
chatOutput.setText("β Error loading model or data.");
return;
}
sendButton.setOnClickListener(v -> {
String text = inputText.getText().toString().trim();
if (text.isEmpty()) return;
new Thread(() -> {
try {
Tokenizer tokenizer = new Tokenizer(vocab);
int[] tokenizedInput = tokenizer.tokenize(text);
int[] paddedInput = new int[MAX_LEN];
for (int i = 0; i < MAX_LEN; i++) {
if (i < tokenizedInput.length) {
paddedInput[i] = tokenizedInput[i];
} else {
paddedInput[i] = 0;
}
}
int[][] inputData = new int[1][MAX_LEN];
inputData[0] = paddedInput;
String inputName = session.getInputNames().iterator().next();
OnnxTensor inputTensor = OnnxTensor.createTensor(env, inputData);
OrtSession.Result result = session.run(Collections.singletonMap(inputName, inputTensor));
float[][] outputArray = (float[][]) result.get(0).getValue();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < outputArray[0].length; i++) {
builder.append("Class: ")
.append(labelMap.get(i))
.append(", Probability: ")
.append(outputArray[0][i])
.append("\n");
}
runOnUiThread(() -> {
chatOutput.append("π€: " + text + "\nπ€:\n" + builder + "\n");
inputText.setText("");
});
} catch (Exception ex) {
ex.printStackTrace();
runOnUiThread(() -> chatOutput.append("β Error during inference\n"));
}
}).start();
});
}
private OrtSession loadModel(String filename) throws IOException, OrtException {
InputStream is = getAssets().open(filename);
byte[] modelBytes = new byte[is.available()];
is.read(modelBytes);
is.close();
return OrtEnvironment.getEnvironment().createSession(modelBytes, new OrtSession.SessionOptions());
}
static class Tokenizer {
private final Map<String, Integer> vocab;
public Tokenizer(Map<String, Integer> vocab) {
this.vocab = vocab;
}
public int[] tokenize(String text) {
String[] words = text.toLowerCase().split("\\s+");
int[] tokenized = new int[words.length];
for (int i = 0; i < words.length; i++) {
tokenized[i] = vocab.getOrDefault(words[i], vocab.get("<OOV>"));
}
return tokenized;
}
}
class LabelVocabLoader {
private final Map<Integer, String> labelMap;
private final Map<String, Integer> vocab;
public LabelVocabLoader(String labelMapFile, String vocabFile) throws Exception {
labelMap = new HashMap<>();
vocab = new HashMap<>();
String labelJson = readJsonFromAssets(labelMapFile);
JSONObject labelObj = new JSONObject(labelJson);
for (Iterator<String> it = labelObj.keys(); it.hasNext(); ) {
String key = it.next();
labelMap.put(Integer.parseInt(key), labelObj.getString(key));
}
String vocabJson = readJsonFromAssets(vocabFile);
JSONObject vocabObj = new JSONObject(vocabJson);
for (Iterator<String> it = vocabObj.keys(); it.hasNext(); ) {
String key = it.next();
vocab.put(key, vocabObj.getInt(key));
}
}
public Map<Integer, String> getLabelMap() {
return labelMap;
}
public Map<String, Integer> getVocab() {
return vocab;
}
private String readJsonFromAssets(String filename) throws IOException {
InputStream is = getAssets().open(filename);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
return sb.toString();
}
}
}
β 5. Create the UI
π Layout Setup
Edit res/layout/activity_main.xml like this:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<EditText
android:id="@+id/inputText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter a sentence..." />
<Button
android:id="@+id/sendButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Run Model" />
<TextView
android:id="@+id/chatOutput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:textAppearanceMedium"
android:paddingTop="16dp" />
</LinearLayout>
β 6. Build the APK
ποΈ Build Steps
In Android Studio:
- Build β Build APK(s) β Build APK
- Once built, click Locate to find your APK:
app/build/outputs/apk/debug/app-debug.apk
β 7. Install on Your Android Device
π² Installation Options
Option 1: USB transfer
- Connect your phone via USB
- Copy app-debug.apk to Downloads
- Open it from your phone and tap Install
- Allow "Install from unknown sources" if prompted
Option 2: Telegram / Google Drive
- Upload the APK
- Download on your phone
- Tap to install
β 8. Android Demo
β 9. Features
- Loads the model from assets
- Loads vocab and label map from JSON
- Tokenizes and pads input text
- Creates an ONNX tensor from input
- Runs inference and shows results
- UI thread-safe updates via runOnUiThread