{"id":50903991,"url":"https://github.com/twinleaf/twinleaf-app","last_synced_at":"2026-06-16T05:30:44.345Z","repository":{"id":363715684,"uuid":"1264428179","full_name":"twinleaf/twinleaf-app","owner":"twinleaf","description":"Twinleaf App (Apple Devices)","archived":false,"fork":false,"pushed_at":"2026-06-10T03:59:42.000Z","size":1897,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-10T05:06:48.784Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/twinleaf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"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-06-09T21:56:16.000Z","updated_at":"2026-06-10T03:59:46.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/twinleaf/twinleaf-app","commit_stats":null,"previous_names":["twinleaf/twinleaf-app"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/twinleaf/twinleaf-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twinleaf%2Ftwinleaf-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twinleaf%2Ftwinleaf-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twinleaf%2Ftwinleaf-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twinleaf%2Ftwinleaf-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/twinleaf","download_url":"https://codeload.github.com/twinleaf/twinleaf-app/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/twinleaf%2Ftwinleaf-app/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34393301,"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-16T02:00:06.860Z","response_time":126,"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":[],"created_at":"2026-06-16T05:30:43.540Z","updated_at":"2026-06-16T05:30:44.338Z","avatar_url":"https://github.com/twinleaf.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Twinleaf\n\nTwinleaf is a native SwiftUI document app for plotting and logging Twinleaf sensor data.\n\nThe macOS app currently targets macOS 26 or newer on Apple Silicon (the project builds arm64 only) so it can use the latest SwiftUI app and toolbar controls. The iPad app is an active port of the same document and plotting surface.\n\nThe app links against Rust for the Twinleaf hardware boundary:\n\n- `Twinleaf`, the single Swift/Xcode app target for both macOS and iPadOS. It owns windows, `.tio` documents, plotting UI, stream selection, and RPC controls.\n- `libtwinleaf_core`, a Rust library loaded dynamically on macOS and linked statically on iPad. It owns all Twinleaf device communication through `twinleaf-rust`, writes raw `.tio` packet logs, runs FPCS/FFT processing, and returns plot frames over a binary callback.\n- `tio-bridge`, a Rust CLI harness that uses the same core code and remains useful for debugging outside the app.\n\nThis keeps the app native while keeping the hardware boundary in Rust, without a subprocess command bridge in the app path.\n\n## Current Shape\n\n- New windows open a device picker.\n- The device picker can remember arbitrary Twinleaf URLs for quick reconnects, with the saved list editable in Settings.\n- The left sidebar lists devices, streams, and columns that can be plotted.\n- The right sidebar lists device RPCs and allows readable RPC refreshes plus simple writable calls.\n- Plotting supports one or more columns.\n- Timeseries display uses the same FPCS-style min/max decimation strategy as Trendline.\n- FFT display uses Welch spectral density through the Rust `welch-sde` crate on a Rust worker thread.\n- Live logging writes Twinleaf packets to a temporary `.tio` backing file. Save or Save As snapshots that backing file into the document path, so an Untitled document can begin logging immediately and keep logging through the save transition.\n- Opening an existing `.tio` file starts in inspection mode: Rust parses the saved packets, stream ID 1 is selected by default, plotting is paused-only, and the toolbar scrubber moves the displayed time window through the log.\n- File \u003e Export writes the raw `.tio` log to CSV or HDF5 through Rust. CSV is available in the default Rust build; HDF5 is available when the Rust core is built with `--features hdf5`.\n- After connecting, the app lazily checks whether newer published firmware exists for each connected device. When an update is available, a green update button appears in the toolbar; its popover shows the installed and new versions and flashes the device with live progress. See the Notes section for the network access this involves.\n\n## Build\n\nBuild the Rust core first:\n\n```sh\ncargo build --manifest-path rust/tio-bridge/Cargo.toml --features hdf5\n```\n\nIf you omit `--features hdf5`, the app can still stream, log, inspect, and export CSV, but HDF5 export will report that Rust was built without HDF5 support.\n\nBuilding with `--features hdf5` compiles HDF5 from source through the statically linked `hdf5-metno` crate, which requires `cmake` on the PATH (`brew install cmake`). No system HDF5 installation is needed.\n\nThen run the macOS app:\n\n```sh\nswift run --build-path build/swiftpm Twinleaf\n```\n\nDuring development, the Swift app loads `rust/tio-bridge/target/debug/libtwinleaf_core.dylib` directly. You can override the library path with `TWINLEAF_CORE_PATH`.\n\nFor Xcode development, open the project and choose the `Twinleaf` scheme:\n\n```sh\nopen Twinleaf.xcodeproj\n```\n\nThe `Twinleaf` Xcode scheme builds the native `Twinleaf.app` bundle for the selected destination. For macOS, it runs `scripts/xcode-build-rust.sh` to package `libtwinleaf_core.dylib` into `Contents/Frameworks` and the `tio-bridge` tool into `Contents/MacOS`. If `cargo` is installed, the script rebuilds the Rust bridge for the active Xcode configuration in an isolated Cargo target directory under `build/xcode-rust`; otherwise it uses existing artifacts from that target directory or the legacy `rust/tio-bridge/target` location.\n\nFor iPadOS, the same target runs `scripts/build-ios-rust.sh` in static-library mode. That script builds `rust/tio-bridge` with serial support disabled and firmware update support enabled, links the resulting `libtwinleaf_core.a` into the app, and supports both iPad devices and iPad simulators. Install the Rust standard libraries once before building for iPad:\n\n```sh\nrustup target add aarch64-apple-ios aarch64-apple-ios-sim\n```\n\nThe iPad app declares local-network usage because live connections use nearby Twinleaf devices or local TIO proxies.\n\nFor normal macOS app behavior, build and launch the `.app` bundle:\n\n```sh\nscripts/build-app.sh\nopen build/Twinleaf.app\n```\n\nThe bundle includes `libtwinleaf_core.dylib` in `Contents/Frameworks`, also includes the `tio-bridge` CLI harness in `Contents/MacOS` for debugging, declares the `.tio` document type, builds Rust with HDF5 export support, and is the preferred way to test menu bar, focus, keyboard shortcuts, and document lifecycle behavior. Running with `swift run --build-path build/swiftpm Twinleaf` remains useful for quick iteration, but it is a command-line launch rather than a full Launch Services app launch.\n\nFor a simulator smoke test of the Xcode iPad bundle, boot an iPad simulator and run:\n\n```sh\nscripts/smoke-ipad-simulator.sh\n```\n\nThe script builds `Twinleaf.app`, installs it into the booted simulator, launches it, saves a screenshot under `build/ipad-simulator-smoke/`, and prints recent app log output.\n\nFor a release-style bundle:\n\n```sh\nscripts/build-app.sh release\nopen build/Twinleaf.app\n```\n\n## Distribution Signing and Notarization\n\nFor direct macOS distribution outside the App Store, install both `Developer ID Application` and `Developer ID Installer` certificates in Keychain, then store notarization credentials once with Apple's `notarytool`:\n\n```sh\nxcrun notarytool store-credentials \"twinleaf-notary\" --apple-id \"you@example.com\" --team-id TEAMID --password \"app-specific-password\"\n```\n\nBuild, sign, notarize, staple, and package release artifacts with:\n\n```sh\nTWINLEAF_NOTARY_PROFILE=twinleaf-notary APPLE_TEAM_ID=TEAMID scripts/release-app.sh\n```\n\nThe script signs the app with hardened runtime, signs the embedded Quick Look extension, Rust dylib, and `tio-bridge` tool, notarizes and staples by default, creates a distributable app ZIP at `build/distribution/Twinleaf-macOS.zip`, builds a signed `/Applications` installer package at `build/distribution/Twinleaf-macOS.pkg`, builds a signed drag-install disk image at `build/distribution/Twinleaf-macOS.dmg`, and exports the iPadOS IPA at `build/distribution/Twinleaf-iPadOS.ipa`. iPadOS export lets Xcode create or update signing assets by default; pass `--no-ios-provisioning-updates` for fully manual signing. If you only want to validate signing locally, use `scripts/release-app.sh --skip-notarization --skip-ios`; use `--skip-pkg`, `--skip-dmg`, or `--skip-ios` to omit those artifacts.\n\nFor company iPads, distribute Twinleaf as an Apple Business Manager custom app. Build an App Store Connect export with:\n\n```sh\nAPPLE_TEAM_ID=TEAMID scripts/release-app.sh --only-ios\n```\n\nThat archives the `Twinleaf` Xcode scheme for an iPadOS destination and exports `build/distribution/Twinleaf-iPadOS.ipa`, suitable for uploading to App Store Connect. From App Store Connect, make the app available as a custom app for the company's Apple Business Manager organization; Apple Business Manager then handles app licenses for MDM assignment. For manual iPadOS signing, pass `--ios-signing-style manual --ios-team-id TEAMID --ios-provisioning-profile \"TwinleafPad App Store\"`.\n\n## Notes\n\nThe project currently vendors `twinleaf-rust` as a Git submodule at:\n\n```text\nvendor/twinleaf-rust\n```\n\nAfter cloning, initialize it with:\n\n```sh\ngit submodule update --init --recursive\n```\n\n### Network access\n\nAfter connecting to a device, the app checks in the background whether newer published firmware exists for it. The check queries the public firmware catalog at `github.com/twinleaf/twinleaf-firmware-updates` through `api.github.com`; the request identifies only the sensor model name and hardware revision. Firmware images are downloaded over HTTPS when you start an upgrade and cached under the OS cache directory (`~/Library/Caches/twinleaf/firmware` on macOS). Apart from this firmware check, the app makes no network connections other than the device and proxy URLs you configure.\n\n## License\n\nTwinleaf app code is licensed under Apache-2.0. See `LICENSE` for the full license text.\n\nVendored dependencies retain their own licenses. In particular, `vendor/twinleaf-rust` is distributed under its upstream MIT/Apache licensing.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwinleaf%2Ftwinleaf-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftwinleaf%2Ftwinleaf-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftwinleaf%2Ftwinleaf-app/lists"}