{"id":50787971,"url":"https://github.com/vkrychun/stem-runtime-swift","last_synced_at":"2026-06-12T09:04:39.647Z","repository":{"id":356558420,"uuid":"1213751010","full_name":"vkrychun/stem-runtime-swift","owner":"vkrychun","description":"Runtime engine for StemJSON DSL","archived":false,"fork":false,"pushed_at":"2026-06-05T12:10:47.000Z","size":5850,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T13:04:50.306Z","etag":null,"topics":["ai","backend-driven-ui","declarative-ui","ios","sdk","server-driven-ui","stemjson","swift","swiftui","xcframework"],"latest_commit_sha":null,"homepage":"https://stemjson.com","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vkrychun.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-17T18:11:40.000Z","updated_at":"2026-06-05T12:10:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vkrychun/stem-runtime-swift","commit_stats":null,"previous_names":["vkrychun/stem-runtime-swift"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/vkrychun/stem-runtime-swift","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vkrychun%2Fstem-runtime-swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vkrychun%2Fstem-runtime-swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vkrychun%2Fstem-runtime-swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vkrychun%2Fstem-runtime-swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vkrychun","download_url":"https://codeload.github.com/vkrychun/stem-runtime-swift/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vkrychun%2Fstem-runtime-swift/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34236583,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","backend-driven-ui","declarative-ui","ios","sdk","server-driven-ui","stemjson","swift","swiftui","xcframework"],"created_at":"2026-06-12T09:04:38.918Z","updated_at":"2026-06-12T09:04:39.640Z","avatar_url":"https://github.com/vkrychun.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# StemRuntimeSDK\n\nYour AI can now ship complete native iOS features, not just code snippets. **StemJSON** is a declarative language describing a full feature — screens, interactions, data, navigation — and **StemRuntimeSDK** runs it as native SwiftUI on-device. AI authors the feature; users get native iOS.\n\n![iOS](https://img.shields.io/badge/iOS-18.0%2B-blue?logo=apple)\n![Swift](https://img.shields.io/badge/Swift-6.0-orange?logo=swift)\n![Xcode](https://img.shields.io/badge/Xcode-26.0%2B-blue?logo=xcode)\n![SPM](https://img.shields.io/badge/SPM-compatible-brightgreen?logo=swift)\n![License](https://img.shields.io/badge/license-Proprietary%20Freeware-lightgrey)\n\n---\n\n## Table of Contents\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Zip-Packaged Modules](#zip-packaged-modules)\n- [Core API](#core-api)\n- [State Observation \u0026 Events](#state-observation--events)\n- [Module Lifecycle](#module-lifecycle)\n- [UIKit Integration](#uikit-integration)\n- [Navigation Embedding](#navigation-embedding)\n- [Custom Repositories](#custom-repositories)\n- [Custom Services](#custom-services)\n- [Error Handling](#error-handling)\n- [Diagnostics \u0026 Logging](#diagnostics--logging)\n- [Module JSON](#module-json)\n- [Thread Safety \u0026 Swift 6 Concurrency](#thread-safety--swift-6-concurrency)\n- [Privacy \u0026 Security](#privacy--security)\n- [Contributing](#contributing)\n- [License](#license)\n\n---\n\n## Requirements\n\n| Dependency | Minimum |\n|---|---|\n| iOS | 18.0 |\n| Swift | 6.0 |\n| Xcode | 26.0 |\n\n---\n\n## Installation\n\n### Swift Package Manager\n\nAdd the package in Xcode via **File › Add Package Dependencies**, or add it to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/vkrychun/stem-runtime-swift.git\", from: \"1.0.2\")\n]\n```\n\n---\n\n## Quick Start\n\n```swift\nimport SwiftUI\nimport StemRuntimeSDK\n\nstruct DashboardView: View {\n    private let runtime = StemRuntime()\n    @State private var stemView: AnyView?\n\n    var body: some View {\n        Group {\n            if let stemView { stemView }\n            else { ProgressView() }\n        }\n        .task {\n            guard\n                let url    = Bundle.main.url(forResource: \"dashboard\", withExtension: \"json\"),\n                let render = try? await runtime.validate(contentsOf: url).get()\n            else { return }\n            stemView = AnyView(render)\n        }\n    }\n}\n```\n\nThree steps in practice: create a runtime, validate a JSON module (file or raw `Data`), embed the returned render — `StemRender` conforms to `View`. The SDK accepts either a single `.json` file or a zip-packaged module and picks the loader from the byte stream — no flag required.\n\n---\n\n## Zip-Packaged Modules\n\nUse a zip when a module needs bundled assets, localisation, or sub-modules.\n\n```\nmy_feature.zip\n├── main.json                  ← required — the module root\n├── details.json               ← sub-module, loaded via file://details.json\n├── localization/\n│   ├── en.strings             ← \"key\" = \"value\"; format\n│   └── uk.strings\n└── assets/\n    └── logo.png               ← loaded via file://assets/logo.png\n```\n\n- Package resources are referenced with `file://\u003crelative-path\u003e` and take precedence over host-app resources with the same path.\n- A zip without `main.json` at the root fails validation.\n- `.strings` files under `localization/` back `l10n://` sources and the `localize(key, fallback)` expression function. The runtime falls back to the host app bundle if a key is missing.\n\nSee [StemJSON Specification §14](https://github.com/vkrychun/StemJSON/blob/main/spec/v1.0.md#14-package--distribution) for the full package format.\n\n---\n\n## Core API\n\n### `StemRuntime`\n\nThe entry point. Create one per app or feature scope.\n\n```swift\n// Default\nlet runtime = StemRuntime()\n\n// With diagnostics\nlet runtime = StemRuntime(.init(enabled: true, minLevel: .warning))\n```\n\nFluent configuration:\n\n```swift\nlet runtime = StemRuntime()\n    .navigationEmbedded()\n    .register(MyRemoteRepository.self, as: StemRepositoryType.remote)\n```\n\n### Validation\n\n```swift\nfunc validate(data: Data, ignore: [StemIssueSeverity] = []) async -\u003e Result\u003cStemRender, StemValidationReport\u003e\nfunc validate(contentsOf url: URL, ignore: [StemIssueSeverity] = []) async -\u003e Result\u003cStemRender, StemValidationReport\u003e\n```\n\n`ignore` suppresses non-critical severity levels from causing a `.failure` (e.g. `[.warning, .note]`).\n\n`StemValidationReport` conforms to `LocalizedError` and `CustomStringConvertible`. Its `description` is a human- and machine-readable report:\n\n```\n=== Validation Report: 2 errors, 1 warning ===\n❌ ERROR | login_btn → onTap | [V002] Value 'repositoryId' is missing\n...\n```\n\nThe format is designed for **AI-in-the-loop authoring**: feed the report back to the model and it will revise the StemJSON module until validation passes.\n\n### `StemRender`\n\nThe value returned by `validate`. It conforms to `View`, `Identifiable`, and `Equatable`, so you can use it in three ways:\n\n```swift\n// 1. Embed in SwiftUI — StemRender is a View\nvar body: some View { render }\n\n// 2. Render in UIKit\nlet vc = runtime.renderViewController(render)\n\n// 3. Read metadata declared in the module's JSON `context`\nlet title: String? = render.title\nlet icon:  String? = render.icon\n```\n\nBeing `Identifiable` and `Equatable` makes it safe to use in `ForEach` and SwiftUI diffing.\n\n---\n\n## State Observation \u0026 Events\n\n### Subscribe to a state key\n\n```swift\nlet cancellable = runtime.subscribe(to: \"cartCount\", in: render) { value in\n    updateBadge(value)\n}\n```\n\n### Stream state changes\n\n```swift\nfor await value in runtime.stream(for: \"cartCount\", from: render) {\n    updateBadge(value)\n}\n```\n\n### Trigger events from native code\n\n```swift\nruntime.trigger(event: \"themeChanged\", data: [\"mode\": \"dark\"])\n```\n\nThe payload is bound into the matching `onCustom` handler's context. Inside the module JSON, read fields as `@{\u003caction.id\u003e.\u003cfield\u003e}`. Always pass every field the handler needs in the payload — path predicates with `@{…}` are not supported inside filter values (see StemJSON spec §6.2.1).\n\n---\n\n## Module Lifecycle\n\nA module cannot terminate itself — it only mutates its own state. The host observes a sentinel state key and calls `kill`:\n\n```swift\nlet cancellable = runtime.subscribe(to: \"onClose\", in: render) { value in\n    guard value as? Bool == true else { return }\n    Task {\n        await runtime.kill(render)\n        isPresented = false\n    }\n}\n```\n\n`kill` is a hard termination — the next `validate` produces a fresh module from initial state. Dismissing without `kill` preserves state so the next open resumes where the user left off.\n\n---\n\n## UIKit Integration\n\n### Embed as a child view controller (recommended)\n\n```swift\nlet render = try? await runtime.validate(contentsOf: url).get()\nlet stemVC = runtime.renderViewController(render!)\naddChild(stemVC)\nview.addSubview(stemVC.view)\nstemVC.view.frame = view.bounds\nstemVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]\nstemVC.didMove(toParent: self)\n```\n\n### Embed as a bare `UIView`\n\nUse only when a child view controller is not possible — `renderViewController` is preferred because it propagates safe-area insets, trait changes, and keyboard avoidance.\n\n```swift\nlet stemView = await runtime.renderView(render)\ncontainerView.addSubview(stemView)\n// pin edges with Auto Layout\n```\n\n---\n\n## Navigation Embedding\n\nBy default a module creates its own `NavigationStack`. When the module is pushed **inside a host navigation flow**, call `.navigationEmbedded()` so internal `navigation` components participate in the host's stack instead:\n\n```swift\nlet runtime = StemRuntime()\n    .navigationEmbedded()\n```\n\nWith this enabled, `link` destinations and `navigate push` actions land on the host's stack; back-swipe and pop operations sync automatically.\n\n\u003e **`link.destination` must have `\"type\": \"module\"`.** A `scroll` / `vstack` placed there renders but its `events` (notably `onAppear`) will not fire. Always wrap pushed layouts as `{ \"type\": \"module\", \"state\": {…}, \"children\": [ … ] }`. See StemJSON spec §link.\n\nDo **not** use `.navigationEmbedded()` for self-contained modules (tab root, modal presentation) — they manage their own navigation.\n\n---\n\n## Custom Repositories\n\nBuilt-in repositories registered automatically:\n\n| Key | Built-in implementation |\n|---|---|\n| `StemRepositoryType.remote` | HTTP/REST |\n| `StemRepositoryType.secured` | Keychain-backed secure storage |\n| `StemRepositoryType.local` | On-device document storage |\n| `StemRepositoryType.photos` | Photo library |\n\nOverride any of them, or register your own under a custom `StemDependencyType`:\n\n```swift\nfinal class ProductRepository: StemRepository {\n    typealias Entity = ProductEntity\n\n    struct Configuration: Decodable, Sendable { let baseURL: String }\n\n    let id: String\n    let config: Configuration\n\n    init(id: String, config: Configuration) throws {\n        self.id = id\n        self.config = config\n    }\n\n    func read(_ input: Entity.Read) async throws(StemActionError) -\u003e Entity.Read.Response { /* … */ }\n    func create(_ input: Entity.Create) async throws(StemActionError) -\u003e Entity.Create.Response { /* … */ }\n    func update(_ input: Entity.Update) async throws(StemActionError) -\u003e Entity.Update.Response { /* … */ }\n    func delete(_ input: Entity.Delete) async throws(StemActionError) -\u003e Entity.Delete.Response { /* … */ }\n}\n\nruntime.register(ProductRepository.self, as: StemRepositoryType.remote)\n```\n\nFor streaming sources (WebSocket, Firestore listener, SSE), also conform to `StemListenable` to back the `listen` action:\n\n```swift\nextension ProductRepository: StemListenable {\n    func listen(_ params: AnyDecodable) -\u003e AsyncThrowingStream\u003cAnyDecodable, Error\u003e { /* … */ }\n}\n```\n\n---\n\n## Custom Services\n\nServices handle operations outside CRUD semantics — analytics, biometrics, camera, location, deep links, health, and so on. The SDK pre-registers `audio` (system sounds and haptics) and `push` (**local notifications only** — for remote push, register your own implementation). Everything else is a host-provided implementation.\n\nConform to `StemService` and implement `execute`:\n\n```swift\nfinal class AnalyticsService: StemService, Decodable {\n    let id: String\n\n    @MainActor\n    func execute(_ input: Any?) async throws(StemActionError) -\u003e Any? {\n        // track event, return value for `output.success`, or nil for fire-and-forget\n        return nil\n    }\n}\n\nruntime.register(AnalyticsService.self, as: StemServiceType.analytics)\n```\n\n`execute` runs on the main actor. Throw `StemActionError` to trigger the `output.failure` chain.\n\nFor dependencies that don't fit the built-in repository or service categories, define a custom key:\n\n```swift\nenum AppDependency: String, StemDependencyType { case featureFlags }\nruntime.register(FeatureFlagService.self, as: AppDependency.featureFlags)\n```\n\n---\n\n## Error Handling\n\nAll SDK errors surface as `StemActionError`, with a typed `StemErrorCode` and a human-readable `message`.\n\n```swift\nlet result = await runtime.validate(data: jsonData)\nswitch result {\ncase .success(let render):  hostView = AnyView(render)\ncase .failure(let report):  print(report.errorDescription ?? report.description)\n}\n```\n\nBuild errors in your own repositories and services with the dedicated initialisers:\n\n```swift\nthrow StemActionError(httpStatusCode: response.statusCode)\nthrow StemActionError(osStatus: keychainStatus)\nthrow StemActionError(.network(.notFound), \"Product \\(id) not found\")\nthrow StemActionError(error, fallback: .unknown)\n```\n\nConform your domain errors to `StemActionErrorConvertible` to let the SDK translate them automatically:\n\n```swift\nextension MyDomainError: StemActionErrorConvertible {\n    func asStemActionError() -\u003e StemActionError { /* … */ }\n}\n```\n\n`StemErrorCode` groups codes into `GeneralError`, `NetworkError`, `StorageError`, `SecurityError`, `FirestoreError`, and a `.custom` bridge for your own types.\n\n---\n\n## Diagnostics \u0026 Logging\n\n```swift\n// Explicit configuration\nlet runtime = StemRuntime(.init(enabled: true, minLevel: .warning))\n\n// Silence\nlet runtime = StemRuntime(.init(enabled: false))\n```\n\nDefaults match the build: `.bingo` in DEBUG, `.warning` in Release. Pass a `Diagnostics.Configuration` explicitly to override.\n\nSeverity levels: `.bingo`, `.info`, `.note`, `.warning`, `.error`, `.critical`.\n\nMessages are emitted through OSLog under the subsystem `com.stem.runtime.sdk`.\n\n---\n\n## Module JSON\n\nStemJSON modules are a declarative tree: every component has a `type`, optional `context`, optional `state`, and optional `children`. Values anywhere in the tree may be static, state-bound (`${field}`), context-bound (`@{key}`), or expression-evaluated (`{{ expr }}`).\n\n```json\n{ \"id\": \"email_field\", \"type\": \"textfield\",\n  \"context\": { \"_label\": \"Email\", \"_text\": \"${email}\" } }\n```\n\nFor the full component catalogue, value syntax, style options, and action types see the [**StemJSON v1.0 Specification**](https://github.com/vkrychun/StemJSON/blob/main/spec/v1.0.md).\n\n### Schema versioning\n\nAdd `\"version\": \"1.0\"` at the module root. The SDK uses it to protect forward compatibility:\n\n| Module vs SDK | Behaviour |\n|---|---|\n| Same or lower | Renders normally |\n| Higher minor | Renders — unknown features show a placeholder |\n| Higher major | Validation fails |\n\nUnknown component types never crash the SDK — they render an informational placeholder and their children still display.\n\n---\n\n## Thread Safety \u0026 Swift 6 Concurrency\n\nThe SDK uses Swift 6 strict concurrency. All public types are `Sendable`.\n\n| Main actor only | Any thread |\n|---|---|\n| Embedding a `StemRender` in a SwiftUI hierarchy | `StemRuntime()` |\n| `renderViewController(_:)` / `renderView(_:)` | `validate(data:)` / `validate(contentsOf:)` |\n| `StemService.execute(_:)` | `subscribe` / `stream` / `trigger` / `kill` / `register` |\n\nCustom repositories and services must declare their `Configuration` and `Response` types `Sendable`.\n\n---\n\n## Privacy \u0026 Security\n\nStemRuntimeSDK runs entirely on-device. It contains no telemetry,\nno analytics, and no phone-home behaviour. The SDK transmits no\ndata to Licensor.\n\nThe SDK ships with a [`PrivacyInfo.xcprivacy`](PrivacyInfo.xcprivacy)\nmanifest declaring only the iOS required-reason APIs it invokes\non-device.\n\nFor your Application, remember to:\n\n1. Add your own `PrivacyInfo.xcprivacy` describing data flows your\n   StemJSON modules cause (Keychain, network requests, etc.).\n2. Set `ITSAppUsesNonExemptEncryption` in your Application's\n   `Info.plist`. For Apps that only use standard iOS encryption APIs\n   (which covers the SDK's anti-tamper hashing), this is typically:\n\n   ```xml\n   \u003ckey\u003eITSAppUsesNonExemptEncryption\u003c/key\u003e\n   \u003cfalse/\u003e\n   ```\n\nTo report a security vulnerability, see [`SECURITY.md`](.github/SECURITY.md).\n\n---\n\n## Contributing\n\nThis repository ships StemRuntimeSDK as a pre-compiled binary. SDK\nsource code is proprietary and is not published here. Bug reports,\ndocumentation fixes, and security disclosures are welcome — see\n[`CONTRIBUTING.md`](.github/CONTRIBUTING.md) and [`SECURITY.md`](.github/SECURITY.md).\n\nThe StemJSON data format was originated and authored by **Vasyl\nKrychun** and is published separately under the Open Web Foundation\nAgreement 1.0 at\n[`github.com/vkrychun/StemJSON`](https://github.com/vkrychun/StemJSON).\n\n---\n\n## License\n\nDistributed under a Proprietary Freeware License. Unlicensed builds display a small \"Powered by StemJSON\" badge on physical devices — its corner is configurable to fit your UI:\n\n```swift\nStemRuntime().watermarkPosition(.topTrailing)\n```\n\nSee [`LICENSE`](LICENSE) for the EULA and\n[`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md) for the\nattribution of embedded open-source components. The StemJSON format\nitself — originated and authored by Vasyl Krychun — is governed by\nthe OWFa 1.0; see the\n[StemJSON spec repo](https://github.com/vkrychun/StemJSON).\n\nPricing: [stemjson.com/sdk/pricing](https://stemjson.com/sdk/pricing).\nCommercial enquiries:\n[vkrychun@stemjson.com](mailto:vkrychun@stemjson.com).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvkrychun%2Fstem-runtime-swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvkrychun%2Fstem-runtime-swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvkrychun%2Fstem-runtime-swift/lists"}