RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/App Dev
App Dev/2026-04-11Intermediate

Gemma 4 for App Development: On-Device AI on the Cutting Edge

Learn how to integrate Google's Gemma 4 E2B and E4B models into iOS and Android apps for on-device AI. Covers privacy, offline capability, low latency, and practical code for real app use cases.

Gemma 42app development40on-device AI5iOS109Android43Rork515mobile AI

Google's Gemma 4, released on April 2, 2026, opens up new possibilities for mobile app developers. The E2B and E4B edge models are specifically designed for device-side deployment — small enough to run on smartphones, yet capable of handling text, images, and audio natively.

Why On-Device AI?

Before getting into implementation, it's worth being clear about why running inference locally is worth the effort.

Privacy by design. When inference happens on the device, user data never leaves it. This is significant for apps handling medical information, personal messages, financial data, or any content subject to privacy regulations like GDPR or HIPAA.

Offline capability. On-device models work without network connectivity. This matters more than it might seem — consider users in aircraft, subways, rural areas, or any situation where connectivity is unreliable.

Latency. Removing the network round-trip means responses feel immediate. For use cases like real-time camera analysis or voice interaction, this is the difference between a smooth experience and a frustrating one.

Cost at scale. Per-token API costs add up quickly at scale. On-device inference shifts the cost to hardware — a one-time consideration per user rather than a recurring operational expense.

Gemma 4 E2B and E4B Specs

Both edge models share the same capabilities:

  • Context window: 128,000 tokens
  • Input modalities: text, images, video, and audio (native)
  • Languages: 140+ natively
  • License: Apache 2.0

E2B is optimized for the smallest possible footprint, suitable for older devices and battery-sensitive deployments. E4B provides higher accuracy for edge use cases where quality matters more than raw efficiency.

iOS Integration via MediaPipe

Google's MediaPipe framework provides the most straightforward path to Gemma 4 on iOS.

import MediaPipeTasksGenAI
import UIKit
 
class GemmaInferenceManager {
    private var llmInference: LlmInference?
    
    func initialize() throws {
        // The model file must be bundled with the app
        guard let modelPath = Bundle.main.path(
            forResource: "gemma-4-e2b-it",
            ofType: "task"
        ) else {
            throw GemmaError.modelNotFound
        }
        
        let options = LlmInference.Options(modelPath: modelPath)
        options.maxTokens = 1024
        options.temperature = 0.7
        options.topK = 40
        
        llmInference = try LlmInference(options: options)
    }
    
    // Text generation
    func generateText(_ prompt: String) throws -> String {
        guard let inference = llmInference else {
            throw GemmaError.notInitialized
        }
        return try inference.generateResponse(inputText: prompt)
    }
    
    // Image + text (multimodal)
    func analyzeImage(_ image: UIImage, question: String) throws -> String {
        guard let inference = llmInference else {
            throw GemmaError.notInitialized
        }
        
        let mpImage = try MPImage(uiImage: image)
        return try inference.generateResponse(
            inputText: question,
            inputImages: [mpImage]
        )
    }
}
 
enum GemmaError: Error {
    case modelNotFound
    case notInitialized
}
 
// Usage example
class ProductScanViewController: UIViewController {
    let gemma = GemmaInferenceManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        try? gemma.initialize()
    }
    
    func processScannedImage(_ image: UIImage) {
        DispatchQueue.global(qos: .userInitiated).async {
            let result = try? self.gemma.analyzeImage(
                image,
                question: "Extract the product name, price, and expiry date from this image. Return as JSON."
            )
            DispatchQueue.main.async {
                // Update UI with result
                print(result ?? "No result")
            }
        }
    }
}

Android Integration

For Android, MediaPipe's Kotlin API provides a clean integration path:

import com.google.mediapipe.tasks.genai.llminference.LlmInference
import com.google.mediapipe.tasks.genai.llminference.LlmInferenceOptions
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
 
class GemmaManager(private val context: Context) {
    
    private var llmInference: LlmInference? = null
    
    suspend fun initialize() = withContext(Dispatchers.IO) {
        // Model file should be downloaded/bundled to internal storage
        val modelPath = "${context.filesDir.path}/gemma-4-e4b-it.task"
        
        val options = LlmInferenceOptions.builder()
            .setModelPath(modelPath)
            .setMaxTokens(1024)
            .setTemperature(0.7f)
            .setTopK(40)
            .build()
        
        llmInference = LlmInference.createFromOptions(context, options)
    }
    
    // Text generation
    suspend fun generate(prompt: String): String = withContext(Dispatchers.IO) {
        llmInference?.generateResponse(prompt) ?: throw IllegalStateException("Not initialized")
    }
    
    // Multimodal: image + text
    suspend fun analyzeImage(bitmap: Bitmap, question: String): String = withContext(Dispatchers.IO) {
        val mpImage = BitmapImageBuilder(bitmap).build()
        llmInference?.generateResponse(question, listOf(mpImage))
            ?: throw IllegalStateException("Not initialized")
    }
    
    fun release() = llmInference?.close()
}
 
// ViewModel integration
class ScanViewModel(application: Application) : AndroidViewModel(application) {
    private val gemmaManager = GemmaManager(application)
    val analysisResult = MutableLiveData<String>()
    
    init {
        viewModelScope.launch {
            gemmaManager.initialize()
        }
    }
    
    fun analyzeReceipt(bitmap: Bitmap) {
        viewModelScope.launch {
            val result = gemmaManager.analyzeImage(
                bitmap,
                "Extract all line items and total amount from this receipt. Return as JSON."
            )
            analysisResult.postValue(result)
        }
    }
    
    override fun onCleared() {
        super.onCleared()
        gemmaManager.release()
    }
}

Rork + Gemma 4 via Local API

If you're building with Rork, you can connect to a local Gemma 4 instance running via LM Studio during development, then switch to Vertex AI for production:

// Rork app: Gemma 4 integration via OpenAI-compatible API
const GEMMA_API_URL = __DEV__
  ? "http://localhost:1234/v1/chat/completions"  // LM Studio for development
  : "https://YOUR_VERTEX_AI_ENDPOINT/v1/chat/completions"; // Production
 
const callGemma4 = async (prompt, imageBase64 = null) => {
  const messageContent = imageBase64
    ? [
        { type: "image_url", image_url: { url: `data:image/jpeg;base64,${imageBase64}` } },
        { type: "text", text: prompt }
      ]
    : prompt;
 
  const response = await fetch(GEMMA_API_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      ...(imageBase64 ? {} : {}) // Add auth header for production
    },
    body: JSON.stringify({
      model: "gemma-4-e4b",
      messages: [{ role: "user", content: messageContent }],
      temperature: 0.7,
      max_tokens: 512
    })
  });
 
  if (!response.ok) throw new Error(`API error: ${response.status}`);
  const data = await response.json();
  return data.choices[0].message.content;
};
 
// Practical usage: auto-tag photos
const tagPhoto = async (photo) => {
  const base64 = await FileSystem.readAsStringAsync(photo.uri, {
    encoding: FileSystem.EncodingType.Base64
  });
  
  const tags = await callGemma4(
    "Identify and list the key subjects, objects, and settings in this photo. Return as a JSON array of strings.",
    base64
  );
  
  return JSON.parse(tags);
};

Practical Use Cases

The combination of on-device inference, multimodal input, and Apache 2.0 licensing enables some compelling app features:

Real-time translation. Point the camera at a sign or menu, and Gemma 4 handles OCR and translation simultaneously. Works offline — critical for travel apps where roaming data may be unavailable.

Voice memo organization. Use E2B/E4B's native audio input to transcribe and categorize voice notes automatically. The on-device processing means no audio ever hits a server.

Receipt and document processing. Photograph a receipt and automatically extract line items, totals, and merchant information into your expense tracking app. No cloud dependency, no privacy concerns.

Healthcare screening support. Apps that analyze photos for preliminary assessments (skin conditions, food portions, exercise form) can use on-device processing to keep sensitive imagery local. (Always with appropriate medical disclaimers.)

Smart camera features. Real-time object identification, text recognition, or scene description overlaid on the camera view — responsive enough to feel native because inference is local.

Performance Considerations

A few things to keep in mind for production deployments:

Model loading takes time. Initialize the model when the app launches or during a natural pause, not on first use. Cache the initialized model in memory across your app session.

Battery impact is real. LLM inference on mobile is compute-intensive. Consider implementing inference budgets — limiting the length and frequency of model calls for less critical features.

Model file size matters. E2B at ~1.5GB and E4B at ~2.5GB are significant downloads. Use on-demand downloading with progress indication rather than bundling in the app binary.

Wrapping up

Gemma 4's E2B and E4B models make on-device AI a practical option for mainstream app development — not a research project. The combination of multimodal input, reasonable model sizes, and an open license removes several barriers that previously made on-device LLMs impractical for most apps.

Whether you're building with native Swift, Kotlin, or Rork, the integration paths are now clear. Start with a focused use case where privacy or offline capability genuinely matters to your users, prove it works, and build from there.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

App Dev2026-06-29
Clipboard UX in Expo apps — copy and paste without flooding users with iOS's paste banner
When you wire up copy and paste with expo-clipboard, iOS's paste permission banner can fire constantly and quietly erode trust. Here's exactly when the banner appears, and how hasStringAsync lets you gate a Paste button without ever reading the contents.
App Dev2026-06-23
Why Wallpapers Look Dull on Device: Taming Display P3 in the Delivery Pipeline
The same wallpaper looked dull once set on device. The culprit was a mix-up between wide-gamut Display P3 and sRGB. Beyond embedding profiles, here is how to tell whether the pixels are truly wide-gamut, a pre-delivery gate script, and the Android wide-color story, across six wallpaper apps.
App Dev2026-05-31
Getting Users All the Way to 'Set as Wallpaper' on iOS — Save-to-Photos Permissions and Shortcuts
iOS apps cannot set the wallpaper directly. Here is how I handle add-only photo permissions, Live Photo saving, guiding users to Settings, and Shortcuts automation, with real numbers from running six wallpaper apps.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →