Expo Modules API 2026: Typovo bezpečné natívne moduly v Swifte a Kotline
Sprievodca Expo Modules API v roku 2026: od create-expo-module scaffoldu cez Function/AsyncFunction DSL, Records a Convertibles až po natívne View, config plugin a upgrade playbook pre platform tímy.
Expo Modules API je moderné rozhranie pre písanie natívnych modulov v Swifte a Kotline, ktoré nahrádza staré ObjC/Java bridge s type-safe DSL, automatickými konverziami argumentov a plnou podporou React Native New Architecture. V roku 2026, po rokoch v produkčnom fintech kóde, je to moja predvolená voľba všade tam, kde nepotrebujem C++. Tento sprievodca ukáže, ako vytvoriť lokálny aj distribučný modul, ako pracovať s Function, AsyncFunction, Record, natívnymi View, eventmi a config plugin-om. Všetko s dôrazom na typovú bezpečnosť a čistotu monorepa.
Expo Modules API generuje bindings zo Swift/Kotlin DSL, takže most (bridge) medzi JS a natívom je celý typovo pokrytý bez ručného JSI/Turbo boilerplate.
create-expo-module scaffolduje monorepo-friendly package so Swift/Kotlin/TS zdrojmi, example appkou a expo-module.config.json, ideálne pre platform tímy.
Record, Enumerable a Convertible protokoly zaručujú, že neplatné JS objekty spadnú s presnou chybou skôr, než sa vôbec dostanú do natívneho kódu.
Expo Modules v roku 2026 bežia priamo cez JSI bez bridge módu a sú plne kompatibilné s Fabric a Turbo Modules na New Architecture.
Ak nepíšete C++ interop, Expo Modules ušetria 60 až 70 % boilerplate oproti čistým Turbo Modules pri porovnateľnej priepustnosti (stovky tisíc volaní/s).
Config plugin umožňuje deklaratívne upravovať Info.plist, AndroidManifest a Gradle bez opustenia Continuous Native Generation workflowu.
Čo je Expo Modules API a prečo ho v roku 2026 preferovať
Expo Modules API je set knižníc, DSL a code-generátorov, ktoré vám umožňujú písať natívny iOS kód v Swifte a natívny Android kód v Kotline, pričom o most, konverzie typov a registráciu v JS runtime sa stará framework. Od chvíle, keď sa bridgeless mode stal defaultom pre React Native 0.79+, dostal moderný Expo Modules ekosystém definitívnu formu. Každý modul beží cez JSI, každá funkcia má explicitnú signatúru a JS strana dostane presne typované volania. V mojom fintech tíme sme za posledný rok migrovali 12 zo 14 interných natívnych balíkov na Expo Modules a počet NSInvalidArgumentException incidentov v Sentrym klesol o 82 %.
Historicky sme písali natívne moduly cez RCTBridgeModule (ObjC) alebo ReactMethod (Java). Chýbala kompilátorová kontrola typov, cez JSON sa serializovalo všetko a null-safety bola len na papieri. Turbo Modules situáciu čiastočne zlepšili cez codegen, ale stále musíte písať flow-typed .js spec, generovať wrappery a manuálne implementovať C++ interfacy.
Expo Modules API to celé skryje. Napíšete Swift Module { … } blok, deklarujete Function("hash") { (input: String) -> String in … } a JS bindingy sa vygenerujú počas buildu. Pre platform tím, ktorý spravuje monorepo s viacerými apkami, je to obrovský posun v konzistencii. Honestly, prvýkrát som tomu neveril, kým som si to nevyskúšal na dvoch reálnych moduloch.
Expo Modules vs Turbo Modules: ktorý si vybrať
Otázka „Expo Modules alebo Turbo Modules?" sa v issue trackeroch objavuje týždenne. Krátka odpoveď: ak nepíšete C++ interop alebo neintegrujete existujúcu C/C++ knižnicu s vlastnou JSI vrstvou, siahnite po Expo Modules. Ak dopĺňate React Native Renderer alebo potrebujete zdieľať kód s natívnym iOS/Android tímom v C++, siahnite po Turbo Modules. Nižšie je porovnanie, ktoré používam v mojich architektúrnych review.
Vlastnosť
Expo Modules API
Turbo Modules
Primárne jazyky
Swift + Kotlin
C++, ObjC++, Java + codegen spec
Boilerplate
Nízky (DSL)
Vysoký (spec + codegen + implementácia)
Typová kontrola
Automatická cez konvertery
Cez ručne písaný Flow/TS spec
C++ interop
Nepodporované
Prvotriedne
JSI runtime
Áno (bridgeless)
Áno (bridgeless)
Config plugin podpora
Vstavaná
Ručne cez expo-config
Priepustnosť volaní/s
~350k
~500k
Krivka učenia
Krátka pre Swift/Kotlin vývojárov
Strmá
V praxi je rozdiel v priepustnosti irelevantný. 99 % modulov nikdy nedosiahne ani 10 000 volaní/s. Rozhodujúce sú maintainability a onboarding time. Náš iOS senior sa naučil Expo Modules DSL za jeden deň; Turbo Modules mu zabrali dva týždne, kým vôbec dokázal pridať novú funkciu do existujúceho modulu. Ak plánujete migráciu na novú architektúru React Native s Fabric a TurboModules, Expo Modules dostanete zadarmo. Sú s ňou plne kompatibilné.
Príprava projektu a scaffolding cez create-expo-module
Predpokladom je Expo SDK 53+, Node 20+ a nainštalovaný Xcode 16 a Android Studio Ladybug Feature Drop. Pre samostatný modul, ktorý budete distribuovať cez npm alebo interné workspace, spustite scaffolding skript v prázdnom priečinku:
npx create-expo-module@latest expo-secure-hash
cd expo-secure-hash
npm run build
npm run open:ios # otvorí príkladovú app v Xcode
npm run open:android # otvorí príkladovú app v Android Studio
Skript sa vás pýta na názov balíka, autora a repozitár. Vytvorí štruktúru so src/ (TypeScript verejné API), ios/ (Swift), android/ (Kotlin), example/ (spustiteľná testovacia app) a expo-module.config.json. Práve expo-module.config.json je manifest, ktorý povie autolinking mechanizmu, aké platformy a moduly balík obsahuje. Nedotýkajte sa jeho platforms poľa manuálne, pridávajte len cez expo-module add.
Srdcom každého Expo modulu je Swift/Kotlin trieda dediaca z Module. Vo vnútri definition() deklarujete verejnú API pomocou DSL. Pozrime sa na iOS implementáciu SHA-256 hasher s podporou soli:
import ExpoModulesCore
import CryptoKit
public class SecureHashModule: Module {
public func definition() -> ModuleDefinition {
Name("SecureHash")
Constants([
"algorithm": "SHA-256",
"version": "1.0.0"
])
Function("hash") { (input: String, salt: String?) -> String in
let payload = (salt ?? "") + input
let digest = SHA256.hash(data: Data(payload.utf8))
return digest.map { String(format: "%02x", $0) }.joined()
}
AsyncFunction("hashFile") { (uri: URL) async throws -> String in
let data = try Data(contentsOf: uri)
let digest = SHA256.hash(data: data)
return digest.map { String(format: "%02x", $0) }.joined()
}
Events("onHashProgress")
}
}
Ekvivalent v Kotline pre Android vyzerá skoro identicky. DSL je zámerne symetrické:
Kľúčový rozdiel oproti Turbo Modules: nikde nepíšete @objc, @ReactMethod, JSI wrapper ani JSON codec. Framework analyzuje typy argumentov a návratovej hodnoty a vygeneruje presné bindings. Function je synchrónny (blokuje JS thread na dobu volania), AsyncFunction beží na background thread pool-e a JS strane vráti Promise. Pre I/O operácie, ako je čítanie súboru, vždy siahnite po AsyncFunction. Inak si zamrznete UI, a áno, presne túto chybu som spravil v mojom prvom module.
TypeScript verejné API
Auto-generované bindings sú v SecureHashModule.web.ts a natívne v SecureHashModule.ts. Vlastný verejný wrapper drží typy pre konzumenta:
Ak potrebujete cez most poslať štruktúrovaný objekt, nikdy nepoužívajte [String: Any] alebo Map<String, Any>. Namiesto toho definujte Record. Príklad: modul, ktorý prijme konfiguračný objekt so soľou, algoritmom a časovým limitom:
import ExpoModulesCore
struct HashOptions: Record {
@Field var algorithm: HashAlgorithm = .sha256
@Field var salt: String? = nil
@Field var iterations: Int = 1
@Field var timeoutMs: Double = 5000
}
enum HashAlgorithm: String, Enumerable {
case sha256 = "SHA-256"
case sha512 = "SHA-512"
case blake3 = "BLAKE3"
}
// v Module { ... }
AsyncFunction("hashWithOptions") { (input: String, options: HashOptions) async throws -> String in
guard options.iterations > 0 else {
throw HashError.invalidIterations
}
// ... implementácia
}
Keď JS strana zavolá hashWithOptions("payload", { algorithm: "SHA-999" }), framework konverziu odmietne s presnou chybou „Cannot convert 'SHA-999' to enum HashAlgorithm" ešte pred vstupom do vášho kódu. To je typová bezpečnosť, ktorú od bridgu v roku 2026 očakávam. Rovnaká vec v Kotline používa data class a @Field anotácie:
class HashOptions : Record {
@Field var algorithm: HashAlgorithm = HashAlgorithm.SHA256
@Field var salt: String? = null
@Field var iterations: Int = 1
@Field var timeoutMs: Double = 5000.0
}
enum class HashAlgorithm(val value: String) : Enumerable {
SHA256("SHA-256"),
SHA512("SHA-512"),
BLAKE3("BLAKE3")
}
Pre neštandardné typy (napr. váš vlastný Money value object) implementujte protokol Convertible a definujte statickú metódu convert(from:). Uvidíte, ako to zjednoduší doménovú vrstvu, keď hash a šifrovanie musia rešpektovať menové a decimal-scale pravidlá.
Natívny View v Swifte a Kotline s propmi a eventmi
Expo Modules API podporuje nielen funkcie, ale aj natívne View komponenty. Predpokladajme, že chceme vlastný podpisový panel (canvas), ktorý na iOS používa PencilKit a na Androide android.graphics.Canvas. Deklarácia v Swifte:
Ak vás zaujíma, ako sa taký komponent správa pod Fabric a čo urobí Reanimated cez shared values, prečítajte si aj náš rozbor optimalizácie štartu React Native aplikácií v roku 2026. Natívne pohľady majú citeľný vplyv na TTI.
Config plugin pre Info.plist a AndroidManifest
Continuous Native Generation znamená, že ios/ a android/ adresáre sú pri každom expo prebuild zmazané a znovu vygenerované. Ak váš modul potrebuje pridať kľúč do Info.plist (napríklad NSCameraUsageDescription) alebo permission do AndroidManifest.xml, musíte to spraviť cez config plugin. Príklad plugin-u, ktorý pridáva do defaultu permission a usage description:
Konzument modulu ho v app.json pridá do plugins poľa a všetka konfigurácia zostáva verzovaná v jednom mieste. Žiadne ručné úpravy Xcode projektu, žiadne merge konflikty v project.pbxproj. Toto je pravdepodobne dôvod č. 1, prečo náš tím prešiel z čistých React Native CLI projektov na Expo. Reprodukovateľné buildy sú v roku 2026 hygiena.
Inline moduly vs. samostatný package v monorepo
Nie každý natívny kód potrebuje samostatný npm balík. Od Expo SDK 53 sú dostupné inline moduly, teda Swift a Kotlin súbory môžete uložiť priamo do modules/ adresára apky a Expo ich autolinkuje ako by boli súčasť package-u. Kedy použiť čo:
Inline modul. Jednorazová funkcionalita používaná v jednej apke, prototypovanie, thin wrapper okolo systémového API bez potreby zdieľania.
Samostatný package. Zdieľanie medzi apkami v monorepo, verejný open-source modul, netriviálna doménová vrstva s testami.
V mojom fintech monorepo držíme dva interné packagey (@bank/native-crypto, @bank/native-nfc) a všetko ostatné je inline. Pravidlo palca: ak sa modul referuje z viac než jednej apky alebo má vlastnú testovaciu suite, dostane vlastný balík. Inline moduly sú výborné pre experimenty, ale nemôžete ich verzovať nezávisle od apky.
Testovanie, ladenie a upgrade playbook
Natívne moduly sa najlepšie testujú v example apke, ktorú create-expo-module vygeneruje. Pre CI odporúčam:
Unit testy Swift/Kotlin v natívnom testovacom targete, izolované od React Native runtime. XCTest a JUnit stačia. Nepotrebujete simulátor.
Integračné testy cez Maestro alebo Detox v example apke. Overia, že bridge funguje end-to-end.
Type-check verejného TS API cez tsc --noEmit. Vyhne sa driftu medzi JS wrapperom a natívnou implementáciou.
Aký je rozdiel medzi Expo Modules API a Turbo Modules?
Expo Modules API poskytuje Swift/Kotlin DSL s auto-generovanými bindings, zatiaľ čo Turbo Modules využívajú codegen z Flow/TS spec a poskytujú prvotriedny C++ interop. Ak nepotrebujete C++ interoperabilitu, Expo Modules znižujú boilerplate o 60 až 70 % pri porovnateľnej priepustnosti.
Sú Expo Modules kompatibilné s React Native New Architecture?
Áno, plne. Všetky Expo Modules bežia priamo cez JSI v bridgeless móde a sú kompatibilné s Fabric renderer-om aj Turbo Modules. Zároveň zachovávajú spätnú kompatibilitu so starou architektúrou pre postupný upgrade.
Môžem používať Expo Modules v bare React Native projekte bez Expo?
Áno. Stačí pridať package expo a povoliť expo autolinking v Podfile a settings.gradle. Nepotrebujete Expo Router ani EAS, aby ste ťažili z Expo Modules API. Je to nezávislá vrstva.
Ako pridám natívnu závislosť ako CocoaPod alebo Gradle knižnicu?
V ios/<ModuleName>.podspec pridajte s.dependency 'PodName', '~> 1.0'. Pre Android v android/build.gradle pridajte do dependencies bloku štandardný implementation "group:artifact:version". Autolinking sa postará o zvyšok.
Kedy použiť inline modul namiesto samostatného package-u?
Inline modul je vhodný pre jednorazovú funkcionalitu používanú len v jednej aplikácii alebo pre rýchle prototypy. Samostatný package si zvoľte, keď potrebujete modul zdieľať medzi apkami v monorepo, chcete nezávislé verzovanie alebo plánujete open-source distribúciu.
Ukážem, ako znížiť React Native cold start zo 4,2 s na 1,6 s pomocou bridgeless módu, Hermes bytecode a inline requires. Reálne merania z 6 produkčných aplikácií, Perfetto trace a konfigurácia pre Expo aj bare projekty.
React Native DevTools nahradili Flipper od verzie 0.76. Ukážem, ako ich otvoriť, používať Performance panel z RN 0.83, profilovať Hermes engine a doplniť stack o Reactotron a Sentry.
Praktický pohľad na novú architektúru React Native v roku 2026: Fabric renderer, TurboModules a JSI v praxi, vrátane migrácie z verzie 0.72 a reálnych benchmarkov.