A native macOS SwiftUI viewer for EDF/BDF biomedical signal files — free, open source, and built for clinicians, researchers, and patients.
EDFbrowser is a widely used open source EDF viewer but requires Qt and does not feel native on macOS. This project brings EDF/BDF viewing to macOS as a first-class native app — no Java, no Wine, no Qt. Just Swift and SwiftUI.
- Native macOS app built with SwiftUI (macOS 13+)
- Opens real EDF and BDF files with a pure Swift parser
- Channel sidebar for selection and navigation
- Waveform rendering using min/max downsampling for smooth zoomed-out views
- Reader abstraction that allows swapping parsers without touching the UI
- Test suite covering parsing, signal processing, and end-to-end waveform reading
The project follows a layered architecture with a clean protocol boundary between the parser and the UI.
┌─────────────────────────────────────────┐
│ UI Layer │
│ ContentView · ViewerViewModel │
│ WaveformMinMaxView · SettingsView │
└────────────────┬────────────────────────┘
│ EDFReading protocol
┌────────────────▼────────────────────────┐
│ Core Layer │
│ │
│ EDFReading (protocol) │
│ ├── MockEDFReader synthetic data │
│ ├── RealEDFReader pure Swift parser │
│ └── EDFlibReader (planned, C-based) │
│ │
│ SignalProcessing min/max downsample │
│ Models ChannelInfo │
│ WaveformWindow │
│ DownsampledWaveform│
└─────────────────────────────────────────┘
Protocol-driven reader (EDFReading) — The UI never knows which parser is active. Any conforming reader can be swapped in at the factory level without touching a single view.
Pure Swift parser (RealEDFReader) — Parses EDF/BDF headers and data records natively. No C dependencies, no bridging headers. Supports both EDF (2-byte samples) and BDF (3-byte samples) with correct digital-to-physical calibration.
Min/max downsampling (SignalProcessing) — Renders millions of samples at any zoom level by computing per-bucket min and max values. This preserves signal peaks that naive decimation would miss.
XCGen (project.yml) — The Xcode project is generated from project.yml using XcodeGen. Do not edit the .xcodeproj directly.
Sources/EDFViewerMac/
├── App/
│ └── EDFViewerMacApp.swift app entry point and scene setup
├── Core/
│ ├── Models.swift ChannelInfo, WaveformWindow, DownsampledWaveform
│ ├── EDFReader.swift EDFReading protocol + MockEDFReader
│ ├── RealEDFReader.swift pure Swift EDF/BDF parser
│ └── SignalProcessing.swift min/max downsampling
└── UI/
├── ContentView.swift root layout with sidebar
├── ViewerViewModel.swift state management and reader coordination
├── WaveformMinMaxView.swift Canvas-based waveform renderer
└── SettingsView.swift user preferences
Tests/EDFViewerMacTests/
├── EDFReaderTests.swift unit tests for Mock and Real readers
├── EDFReadIntegrationTests.swift end-to-end parse and downsample tests
├── SignalProcessingTests.swift downsampling correctness
└── ViewerViewModelTests.swift ViewModel state tests
- macOS 13 or later
- Xcode 15 or later
- XcodeGen — generates the
.xcodeprojfromproject.yml
Note:
.xcodeprojis not committed to the repo. You must generate it locally before opening in Xcode.
1. Install XcodeGen
brew install xcodegen2. Clone the repo
git clone https://github.com/your-org/EDFViewer-MacOS.git
cd EDFViewer-MacOS3. Generate the Xcode project
xcodegen generateThis reads project.yml and produces EDFViewerMac.xcodeproj. Re-run this any time project.yml changes (e.g. after pulling new files or adding targets).
4. Open in Xcode and run
open EDFViewerMac.xcodeprojPress Cmd+R to build and run.
5. Try the sample file
A sample EDF file is included in Samples/combined-sample.edf. Use File → Open in the app to load it and verify everything is working.
xcodebuild test -project EDFViewerMac.xcodeproj -scheme EDFViewerMacThis repo includes XcodeGen signing config files and a release script:
configs/Debug.xcconfigconfigs/Release.xcconfigscripts/release/release_dmg.sh
Required environment variables:
export APPLE_TEAM_ID="YOUR_TEAM_ID"
export APPLE_ID="your-apple-id@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"Build, sign, notarize, and staple a DMG:
./scripts/release/release_dmg.shOutput:
build/release/EDFViewer.dmg
The workflow at .github/workflows/release.yml will:
- build an unsigned DMG if signing secrets are missing
- build, sign, notarize, and staple the DMG if secrets are present
Add these repository secrets:
APPLE_TEAM_IDAPPLE_IDAPPLE_APP_SPECIFIC_PASSWORDMACOS_CERT_P12_BASE64MACOS_CERT_PASSWORDMACOS_KEYCHAIN_PASSWORD(optional, recommended)
Create MACOS_CERT_P12_BASE64 from your Developer ID certificate export:
- Open Keychain Access
- Export your Developer ID Application cert + private key as
.p12 - Convert to base64:
base64 -i developer_id_application.p12 | pbcopyPaste that copied value into MACOS_CERT_P12_BASE64.
Flow:
developbranch pushes -> temporary DMG workflow (release-temp-dmg.yml)maintags (v*) -> signed + notarized release workflow (release.yml)
Use workflow:
.github/workflows/release-temp-dmg.yml
How to run:
- GitHub -> Actions
- Choose Release Temp DMG (or push to
developto run automatically) - Click Run workflow
- Download
temp-dmgartifact
This build is ad-hoc signed for testing only and may show Gatekeeper warnings on other Macs.
- Native SwiftUI macOS app
- Pure Swift EDF/BDF parser with digital-to-physical calibration
- Channel sidebar and waveform rendering
- All Channels stacked montage view
- Min/max downsampling for performant rendering
- Pan with boundary clamping (arrows disable at file edges)
- Time-axis grid overlay
- Unit and integration test suite
- Per-channel amplitude scaling
- Keyboard navigation (arrow keys, scroll wheel zoom)
- Large-file IO redesign (mmap + LRU cache)
- Header inspector panel
- EDF+ annotation channel parsing (TAL)
- Annotation overlay, list panel, and editor
- Crosshair cursor with time + amplitude readout
- Rectangle zoom
- Notch + bandpass filters
- FFT / power spectrum view
- Montage editor with 10-20 EEG presets
- Pan-Tompkins QRS detector
See ROADMAP.md for the full milestone plan and EDFbrowser feature comparison.
Pull requests are welcome. Please open an issue first for significant changes.
The EDFReading protocol is the right place to add new parser backends — conform to it and update EDFReaderFactory. The UI does not need to change.
MIT License. See LICENSE for details.
edflib.c and edflib.h by Teunis van Beelen are licensed under BSD 3-clause and retain their original copyright headers.
"Whatever you do, work at it with all your heart, as working for the Lord, not for human masters." — Colossians 3:23
"Heal the sick, cleanse the lepers, raise the dead, cast out devils: freely ye have received, freely give." — Matthew 10:8
