macOS Automation Performance: AppleScript vs Swift

macos · automation · performance|2025-11-18 · 4 min read

Problem

Calendar event queries using AppleScript were unacceptably slow for daily workflow automation:

  • Target: Sub-second response time for morning paper generation
  • Reality: 81 seconds to query a single day's events
  • Impact: Would block entire morning paper workflow

Optimization Journey

Attempt 1: Optimize AppleScript Algorithm

Changes made:

  • Direct calendar lookup instead of iterating all calendars
  • Insertion sort replacing bubble sort (O(n²) → O(n²) best case O(n))
  • Numeric sort keys (minutes since midnight) vs string comparison
  • List building + join instead of string concatenation

Result: ~81 seconds (no improvement)

Learning: AppleScript's fundamental overhead from bridging to Calendar.app cannot be optimized away with algorithmic improvements.

Attempt 2: Swift Script (Interpreted)

Implementation:

#!/usr/bin/env swift
import EventKit
// ... query using native EventKit framework

Result: 5.1 seconds (16x faster)

Learning: Native framework access is significantly faster than AppleScript bridging, but Swift script interpretation adds overhead.

Attempt 3: Compiled Swift Binary

Implementation:

swiftc -O list-events.swift -o list-events

Result: 0.067 seconds (1,209x faster than AppleScript, 76x faster than interpreted Swift)

Final speedup: 1,209x improvement

Performance Comparison

Implementation Time (seconds) Speedup Use Case
AppleScript (original) 81.0 1x ❌ Too slow
AppleScript (optimized) 81.0 1x ❌ Still too slow
Swift script (interpreted) 5.1 16x ⚠️ Acceptable for one-off scripts
Swift binary (compiled) 0.067 1,209x Production workflows

Testing Methodology

Performance Testing

# Use 'time' to measure execution
time bash list.sh 2025-11-19
 
# Test multiple dates for consistency
for date in 2025-11-18 2025-11-19 2025-11-20; do
    echo "=== $date ==="
    time bash list.sh "$date"
done

Validation Testing

# Verify output correctness
bash list.sh 2025-11-19
 
# Compare outputs between implementations
diff <(applescript-version) <(swift-version)
 
# Test edge cases
bash list.sh 2025-01-01  # New Year's Day
bash list.sh             # Today (default)

Iterative Approach

  1. Establish baseline - Measure original implementation
  2. Try algorithmic optimization first - Cheaper than rewrite
  3. If insufficient, change technology - Swift for native access
  4. Optimize the new approach - Compile for production use
  5. Validate thoroughly - Ensure correctness maintained

Key Learnings

When to Use AppleScript

  • Simple one-off automation tasks
  • GUI scripting (no Swift equivalent)
  • When <5 second execution is acceptable
  • Prototyping before Swift implementation

When to Use Swift + EventKit

  • Performance-critical workflows (daily automation)
  • Need <1 second response time
  • Querying large datasets (calendar events, contacts, etc.)
  • Production automation scripts

Technical Insights

AppleScript limitations:

  • High bridging overhead to native apps
  • Single-threaded execution
  • String manipulation is slow
  • Cannot be meaningfully optimized for performance

Swift advantages:

  • Direct framework access (EventKit, Contacts, etc.)
  • Compiled to native code
  • Modern language features
  • Same APIs used by native macOS apps

Compilation matters:

  • Interpreted Swift: 5.1 seconds
  • Compiled Swift: 0.067 seconds
  • 76x speedup from compilation alone

Implementation Pattern

Structure

scripts/
├── list.sh              # Shell wrapper (applies filtering, error handling)
├── list-events.swift    # Swift source (version controlled)
├── list-events          # Compiled binary (gitignored)
└── README.md            # Setup/rebuild instructions

First-time setup

cd scripts/
swiftc -O list-events.swift -o list-events

Usage

bash list.sh              # Calls compiled binary
bash list.sh 2025-11-20   # Specific date

Recommendations

For Daily Workflows

  1. Always compile Swift scripts - 76x speedup over interpreted
  2. Use swiftc -O - Optimization flag critical for performance
  3. Version control source, not binary - Binaries are platform-specific
  4. Provide compilation instructions - README.md for future maintenance

For Quick Prototypes

  1. Start with AppleScript - Faster to write
  2. Measure performance early - Before building workflows around it
  3. Set performance budgets - If >1 second for daily tasks, consider Swift
  4. Plan for migration - Design shell wrapper that can swap implementations

Performance Testing

  1. Use time command - Built-in, reliable measurement
  2. Test multiple iterations - Rule out caching, first-run overhead
  3. Test with real data - Production calendar size, not empty calendar
  4. Document baseline - Before optimizing, measure current state

Future Applications

This pattern applies to other macOS automation:

  • Contacts queries - ContactsKit framework
  • Reminders - EventKit (same framework as Calendar)
  • Photos - PhotoKit framework
  • Files/metadata - FileManager, Spotlight APIs
  • System info - IOKit, SystemConfiguration

Rule of thumb: If AppleScript takes >1 second, rewrite in Swift with native frameworks.