SwiftFlash is a lightweight, native macOS application for flashing .iso file to a external drive . Built with SwiftUI and Swift 6, SwiftFlash aims to be a minimal, safe, and open-source alternative to tools like balenaEtcher or Raspberry Pi Imager.
β‘οΈ Simple. Safe. Swift.
- π₯οΈ Native macOS Interface - Built with SwiftUI for seamless integration and a very small app size (just around 4MB)
- π Drag & Drop Support - Simply drag your image file onto the app
- πΎ Device Inventory - Tracks and remembers your external drives (e.g. USB sticks, SD Cards ...)
- The app provide and inventory of all externale devices and the user can define a name and a type.
- A automatic type detection is for the moment not possibe as related informaton are missing. SHA256 Checksum - Generate and store integrity checksums
- LocalAuthentication to provide access to root rights (flash is only possible under root)
.iso- ISO disk images
- macOS: 15.6 or later
- Architecture: Apple Silicon (ARM64) or Intel (x86_64)
Download the latest release from the Releases page.
git clone https://github.com/yourusername/SwiftFlash.git
cd SwiftFlash
xcodebuild -project SwiftFlash/SwiftFlash.xcodeproj -scheme SwiftFlash -configuration Release build- Select Image File - Drag and drop your image file, or use the file picker
- Choose Target Drive - Select the external drive from the list of available drives
- Start Flashing - Click "Flash", confirm and wait for completion
SwiftFlash includes built-in SHA256 checksum functionality to ensure file integrity and verify downloaded images.
- Sandboxing: Sandboxing is for the moment disabled as the access to flash drive require root rights.
- Drive Validation - Only removable drives are shown as targets
- System Drive Protection - System drives are automatically excluded
- Confirmation Dialogs - Multiple confirmation steps before flashing
- Read-only Detection - Warns about read-only drives
- Language: Swift 6
- Target: macOS 15.6+
- UI Framework: SwiftUI
- Binary Size: Optimized for minimal footprint
- Documentation: Function-level comments and key step documentation
- Dependencies: Prefer system libraries over third-party dependencies
- APIs: Use latest libraries and avoid deprecated interfaces
- Design Principles :
- Unit: Define features in units of functionality
- Services : Services shall be used to encapsulate business logic and provide a clear separation of concerns
- Model : The model shall be used to represent the data and business logic
- Incjection : The main app shall use dependency injection to provide the services to the view models.
- Protocols : Protocols shall be used to define the interface of the services and the model.
- Views : Views shall be defined as generic as possible to be reused as much as possible.
- ViewModels : ViewModels shall be used to provide the data and business logic to the views (optional)
- Coordinators : Coordinators shall be used to manage the flow of the application (TODO)
- Error Handling : Error handling shall be implemented to provide a good user experience.
- Localization : Localization shall be implemented to support multiple languages (TODO)
- Testing : Unit tests shall be implemented to ensure the quality of the code.
- Modules : The app shall be divided into modules to improve maintainability and scalability (TODO)
- Mockup : Modules shall provide mockups to test the functionality of the app.
see Structure
SwiftFlash uses a date-based versioning scheme with automated build number generation based on git.
see Versioning for more.
- Disk Arbitration Framework - Native macOS disk management
- IOKit - Hardware interface and device properties
- SwiftUI - Modern declarative UI framework
- Combine - Reactive programming for data flow
- CryptoKit - SHA256 checksum generation and verification
- Clone the repository
- Open
SwiftFlash.xcodeprojin Xcode - Select your target device/simulator
- Build and run
1 SwiftUI's testing framework initializes the complete application context, which means:
- The main
SwiftFlashAppstruct is instantiated AppModeland all its dependencies are created- Services like
DriveDetectionServiceare initialized with real system resources - Debug logging from app initialization appears in test output
When running testDetectDrivesExcludesDiskImages, you'll see:
π [DEBUG] SwiftFlash App Starting
π [DEBUG] Found X external storage devices
π [DEBUG] Checking device...
This occurs because:
SwiftFlashApp.init()prints the startup messageAppModelinitializesDriveDetectionServiceDriveDetectionService.detectDrives()performs real IOKit operations with debug logging
2 5 To achieve proper unit testing isolation:
Protocol-Based Abstraction:
protocol DriveDetectionProtocol {
func detectDrives() async throws -> [Drive]
}
class DriveDetectionService: DriveDetectionProtocol {
// Real implementation using IOKit
}
class MockDriveDetectionService: DriveDetectionProtocol {
// Mock implementation for testing
}Dependency Injection in Services:
class AppModel: ObservableObject {
private let driveDetection: DriveDetectionProtocol
init(driveDetection: DriveDetectionProtocol = DriveDetectionService()) {
self.driveDetection = driveDetection
}
}4 For SwiftUI views, use @EnvironmentObject for clean dependency injection:
@main
struct SwiftFlashApp: App {
let appModel = AppModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appModel)
}
}
}1 To prevent full app initialization during tests:
@main
struct SwiftFlashApp: App {
var body: some Scene {
WindowGroup {
// Only initialize full app when not running tests
if NSClassFromString("XCTestCase") == nil {
ContentView()
.environmentObject(AppModel())
} else {
Text("Test Mode")
}
}
}
}3 For testing async operations without complex mocking:
class ProductLoader {
private let loadProduct: (Product.ID) async throws -> Product
init(loadProduct: @escaping (Product.ID) async throws -> Product = Self.defaultLoadProduct) {
self.loadProduct = loadProduct
}
func load(id: Product.ID) async throws -> Product {
try await loadProduct(id)
}
}For tests that require real hardware interaction (like testDetectDrivesExcludesDiskImages):
- Environment Gating: Use
SWIFTFLASH_HW_TESTS=1to control execution - Clear Documentation: Mark tests as hardware-dependent
- Separate Test Targets: Consider separate targets for unit vs. integration tests
- CI/CD Considerations: Hardware tests may need special runners
- Pure Unit Tests: Test business logic with mocked dependencies
- Integration Tests: Test service interactions with real or stubbed backends
- Hardware Tests: Test actual device detection and interaction
- UI Tests: Test complete user workflows
- Implement dependency injection container
- Create mock implementations for all external services
- Add conditional compilation flags for test vs. production builds
- Consider using a logging framework with configurable levels
- Separate unit tests from integration/hardware tests
This project is licensed under the MIT License - see the LICENSE (LICENSE) file for details.
- Built with β€οΈ using Swift and SwiftUI
- Inspired by the need for a lightweight, native macOS flashing tool
- Thanks to the macOS Disk Arbitration framework for reliable disk management
- Special thanks to Claude Sonnet 4 for AI-assisted development and code improvements
- Developed using Cursor - the AI-first code editor
SwiftFlash - Making disk flashing simple and safe on macOS.