DMGMaker: Creating Beautiful Disk Images for macOS App Distribution

If you've ever shipped a macOS app outside the App Store, you know the pain: creating a professional-looking DMG (disk image) file for distribution. The traditional approach involves arcane hdiutil commands, cryptic AppleScript incantations, and a lot of trial and error to get that perfect "drag-to-Applications" experience.

I wanted something better. Something that lets developers see exactly what their DMG will look like before they create it. Something that makes the whole process as simple as drag-and-drop.

So I built DMGMaker.

DMGMaker Main Screen

Quick Links

What is DMGMaker?

DMGMaker is a native macOS application built with SwiftUI that creates beautiful, professional disk images for distributing macOS applications. The core philosophy is simple: Drop → Preview → Create.

The app turns anything you drop in—most commonly a .app bundle—into a polished "drag-to-Applications" DMG with excellent defaults, minimal configuration, and a pixel-perfect preview of exactly what users will see when they mount your DMG.

DMGMaker Landing Screen

Features

Core Functionality

  • Drag-and-Drop Input: Drop .app bundles, .zip archives, or folders directly into the app
  • Intelligent Parsing: Automatically extracts app name, version, bundle identifier, and icon from Info.plist
  • Pixel-Perfect Preview: What you see in the app is exactly what users see when they open your DMG
  • One-Click Export: Generate professional DMGs with a single click

Background Design System

DMGMaker includes a sophisticated background generation system with four built-in styles:

  1. Gradient: Classic linear gradients with colors extracted from your app icon
  2. Glass (Glassmorphism): Modern frosted glass effect with blur orbs
  3. Solid: Simple solid color backgrounds
  4. Mesh Gradient: Beautiful overlapping radial gradients for a modern look

Each style automatically adapts to your app's color palette by extracting dominant colors from the app icon using Core Image's CIAreaAverage filter.

Apple Intelligence Integration

On macOS 26.0 and later, DMGMaker integrates with Apple's Image Playground API to generate AI-powered backgrounds. Simply click the AI button, and the app will use your app icon and name as concepts to create a unique, themed background.

Image Playground Integration

Canvas Shapes & Annotations

The app includes a complete shape editor for adding visual elements to your DMG backgrounds:

  • Rectangles & Rounded Rectangles: With customizable corner radius
  • Dotted Frames: Perfect for indicating drop zones
  • Arrows: Straight arrows with multiple head styles (triangle, chevron, rounded)
  • Text Labels: With gradient fills, shadows, glow effects, and custom typography

Each shape supports:

  • Fill colors with opacity
  • Stroke styles (solid, dotted, dashed)
  • Glow effects
  • Drop shadows

Text and Arrow Styling

Text Styling

The text system is particularly powerful:

  • Gradient text fills with presets like Silver Metal, Gold Metal, Rose Gold, and Copper
  • Font customization: family, weight (ultra light to black), size
  • Typography controls: tracking (letter spacing), line height
  • Effects: shadow, glow, stroke outline
  • Alignment: left, center, right, justified

Template System

Save your designs as reusable templates:

  • Store window size, background settings, icon positions, and shapes
  • Auto-match templates by bundle identifier
  • Quick-apply to new projects

Export Options

  • DMG Export: Create compressed disk images with multiple compression levels:
    • None (read-write, UDRW format)
    • Fast (zlib compression, UDZO format)
    • Best (lzfse compression, ULFO format)
  • PNG Export: Export backgrounds as high-resolution PNG files (with or without icons)
  • Project Files: Save and load .dmgmaker project files

Quick Look Preview

DMGMaker includes a Quick Look extension that lets you preview .dmgmaker project files directly in Finder. Press Space on any project file to instantly see:

  • The full DMG preview with background, icons, and shapes
  • App name and version information
  • No need to open the app just to remember what a project looks like

This makes managing multiple DMG projects effortless—browse through your saved designs without leaving Finder.

Note: The Quick Look extension currently works when running DMGMaker from Xcode. I'm still figuring out how to make it work properly when the app is distributed outside of the development environment—stay tuned for updates.


Technical Deep Dive

Architecture

DMGMaker follows the MVVM (Model-View-ViewModel) pattern:

Models/
├── AppBundle.swift        # Parsed app bundle metadata
├── CanvasShape.swift      # Shape definitions and styling
├── DMGProject.swift       # Project state and configuration
└── DMGTemplate.swift      # Template definitions

ViewModels/
└── DMGProjectViewModel.swift  # Main business logic

Services/
├── AIBackgroundGenerator.swift     # Apple Intelligence integration
├── AppBundleParser.swift           # Info.plist parsing
├── BuiltInBackgroundGenerator.swift # Core Image backgrounds
├── CompositeBackgroundRenderer.swift # Final image composition
├── DMGBuilder.swift                # DMG creation orchestration
├── DSStoreWriter.swift             # Finder metadata writing
├── FileAccessManager.swift         # Sandbox file access
└── TemplateManager.swift           # Template persistence

DMG Creation Pipeline

The DMG creation process is a multi-step pipeline:

  1. Create Temporary DMG: Use hdiutil create to make a read-write HFS+ disk image
  2. Mount Volume: Attach the DMG using hdiutil attach
  3. Copy Contents: Copy the app bundle and create the Applications symlink
  4. Render Background: Composite background + shapes + text into a single PNG
  5. Style Finder Window: Use AppleScript to configure icon positions, window size, and background
  6. Unmount: Detach the volume
  7. Compress: Convert to final format using hdiutil convert
  8. Verify: Run hdiutil verify to ensure integrity
private func createTemporaryDMG(at path: URL, volumeName: String, size: Int) async throws {
    // Sanitize volume name - remove special characters that cause issues
    let sanitizedVolumeName = volumeName
        .replacingOccurrences(of: "/", with: "-")
        .replacingOccurrences(of: ":", with: "-")
        .trimmingCharacters(in: .whitespacesAndNewlines)
    
    // Create a read-write HFS+ DMG
    // hdiutil create -size 500m -fs HFS+ -volname "VolumeName" temp.dmg
    try await runHdiutil([
        "create",
        "-size", "\(size)m",
        "-fs", "HFS+",
        "-volname", sanitizedVolumeName.isEmpty ? "Install" : sanitizedVolumeName,
        path.path
    ])
}

private func mountDMG(at path: URL) async throws {
    // hdiutil attach temp.dmg
    try await runHdiutil(["attach", path.path, "-nobrowse"])
}

The Finder Window Challenge

One of the trickiest parts of DMG creation is getting the Finder window to display correctly. The app uses AppleScript to configure the window:

tell application "Finder"
    tell disk "VolumeName"
        open
        tell container window
            set current view to icon view
            set toolbar visible to false
            set statusbar visible to false
            set sidebar width to 0
        end tell
        set theViewOptions to the icon view options of container window
        set arrangement of theViewOptions to not arranged
        set icon size of theViewOptions to 128
        set background picture of theViewOptions to file ".background:background.png"
        set position of item "MyApp.app" to {160, 220}
        set position of item "Applications" to {500, 220}
        set the bounds of container window to {100, 100, 760, 608}
        close
        open
        update without registering applications
    end tell
end tell

Background Generation with Core Image

The built-in background generator uses Core Image filters extensively:

// Generate gradient background
let filter = CIFilter.linearGradient()
filter.point0 = CGPoint(x: 0, y: size.height)
filter.point1 = CGPoint(x: size.width, y: 0)
filter.color0 = CIColor(color: startColor)
filter.color1 = CIColor(color: endColor)

// Create blur orbs for glassmorphism
let orbFilter = CIFilter.radialGradient()
let blurFilter = CIFilter.gaussianBlur()

// Add noise texture for premium feel
let noiseFilter = CIFilter.randomGenerator()

Color Extraction

To make backgrounds match the app's theme, DMGMaker extracts dominant colors from the app icon:

func extractDominantColors(from image: NSImage, count: Int = 4) -> [NSColor] {
    // Sample from multiple regions
    let samplePoints = [(0.25, 0.25), (0.75, 0.25), (0.25, 0.75), (0.75, 0.75), (0.5, 0.5)]
    
    for point in samplePoints {
        // Use CIAreaAverage to get mean color in region
        let filter = CIFilter.areaAverage()
        filter.inputImage = ciImage
        filter.extent = sampleRect
        // Extract RGBA values...
    }
    
    // Remove similar colors to ensure variety
    return removeSimilarColors(colors)
}

Shape Rendering

Shapes are rendered in layers using SwiftUI's Canvas API, then composited with the background for export:

// Arrow rendering with proper head geometry
func drawArrow(context: GraphicsContext, geometry: ArrowGeometry, style: ShapeStyle) {
    var path = Path()
    
    // Calculate arrow head points based on angle
    let angle = geometry.angle
    let headSize = geometry.headSize
    
    // Shaft
    path.move(to: geometry.start)
    path.addLine(to: shaftEnd)
    
    // Arrow head (triangle, chevron, or rounded)
    switch geometry.headStyle {
    case .triangle:
        path.move(to: geometry.end)
        path.addLine(to: leftPoint)
        path.addLine(to: rightPoint)
        path.closeSubpath()
    // ...
    }
    
    // Apply glow effect
    if style.glow.isEnabled {
        context.addFilter(.shadow(color: glowColor, radius: style.glow.radius))
    }
    
    context.stroke(path, with: .color(style.stroke.color.color))
}

Project File Format

DMGMaker uses a JSON-based project format (.dmgmaker):

struct DMGMakerDocument: Codable {
    var project: DMGProject
    var formatVersion: Int = 2
}

The format stores:

  • All project settings (window size, icon positions, etc.)
  • Background image as compressed PNG
  • Shape definitions with full styling
  • Template associations

Notably, the app icon is not saved in the file—it's re-extracted from the app bundle on load, keeping file sizes small.


Development Process

SwiftUI for macOS

Building a complex macOS app with SwiftUI was both rewarding and challenging. Some highlights:

What worked well:

  • NavigationSplitView for the sidebar layout
  • Canvas API for custom drawing
  • @Published properties for reactive UI updates
  • Drag-and-drop with NSItemProvider

Challenges:

  • Managing focus state for text editing
  • Coordinate space conversions between views
  • com.apple.security.app-sandbox restrictions when executing shell commands

Sandboxing Considerations

DMGMaker needs to:

  • Read dropped app bundles
  • Access the Applications folder (for icon extraction)
  • Write to user-selected locations
  • Execute hdiutil and AppleScript

This required careful entitlement configuration and the FileAccessManager service to handle bookmark persistence.

Preview Accuracy

The hardest part was ensuring the preview exactly matches the final DMG. Key insights:

  1. Title bar height: Finder windows have a 28pt title bar that affects content area calculations
  2. Icon positioning: Finder positions icons by their center, not top-left corner
  3. Label offset: The icon label adds to the total height, requiring position compensation
  4. Scale factors: Retina backgrounds need 2x rendering, but icon positions are in 1x coordinates

What's Next

Future enhancements I'm considering:

  • Performance improvements
  • Lot's of small bugs
  • Curved Bézier arrows
  • Blur/frosted glass effects on shapes fix (it's different in preview than in .dmg)
  • CLI companion for CI/CD integration 🚀
  • Code signing and notarization guidance

Conclusion

Building DMGMaker was a journey through many macOS technologies: SwiftUI, Core Image, AppleScript, hdiutil, and even Apple Intelligence. The result is an app that takes the pain out of DMG creation while giving developers full creative control over their app's first impression.

If you're shipping macOS apps outside the App Store, I hope DMGMaker makes your life a little easier—and your disk images a lot more beautiful.


DMGMaker is built with SwiftUI for macOS 15+ (with Apple Intelligence features requiring macOS 26+).


Daniel Wanja is a developer and founder of Nouvelles Solutions, Inc. He builds native macOS apps as part of his ongoing exploration of AI-assisted software development.