πŸ’» 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:

  1. File β†’ New β†’ Project β†’ iOS β†’ App
  2. Name it: ONNXruntime
  3. 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
  1. Open ONNXruntime.xcworkspace
  2. Select a simulator or your connected iPhone
  3. 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)

iOS Demo

🧠 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 model
  • vocab.json – dictionary mapping words to token IDs
  • labelMap.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:

  1. Build β†’ Build APK(s) β†’ Build APK
  2. 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

  1. Connect your phone via USB
  2. Copy app-debug.apk to Downloads
  3. Open it from your phone and tap Install
  4. Allow "Install from unknown sources" if prompted

Option 2: Telegram / Google Drive

  1. Upload the APK
  2. Download on your phone
  3. Tap to install

βœ… 8. Android Demo

πŸ€–

Android Demo

🎬 ONNX inference running on Android device

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