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.