iPhoneCopy is a command-line utility for Windows that copies photos and videos from an iPhone connected via USB cable to a local folder on the PC. It is designed to run unattended overnight across libraries of 20,000+ files, behaving like robocopy: incremental copies, fault-tolerant, resumable after interruption, with detailed logging.
Windows has no built-in tool to reliably sync photos/videos from an iPhone in bulk. The Photos app and iCloud are not suitable for users who want a local, scripted, or automated copy workflow. iTunes/Finder sync is overkill and not scriptable. Robocopy works great for local/network paths but cannot reach the iPhone's file system.
- Copy all photos and videos from the iPhone's DCIM folder to a local destination path
- Skip files that already exist at the destination (incremental copy)
- Resume a previous run after interruption without re-copying already-transferred files
- Survive individual file errors without aborting the entire run
- Display clear progress: files copied, skipped, failed, bytes transferred, ETA
- Produce a single standalone
.exe— no installer, no dependencies to manage - Work without jailbreaking the iPhone
- Work on Windows 10/11
- Syncing back to the phone
- Deleting files from the phone after copy
- Managing Apple Music, contacts, or app data
- Cross-platform support (macOS/Linux) in v1
- GUI interface in v1
| # | Requirement |
|---|---|
| F1 | Detect iPhone(s) connected via USB and list them if multiple are present |
| F2 | Enumerate all photo/video files in the DCIM folder lazily (streaming, not loading all into memory at once) |
| F3 | Copy files to a user-specified local destination directory, preserving DCIM subfolder structure by default |
| F4 | Skip files that already exist at the destination with the same name and size |
| F5 | Support --overwrite flag to force re-copy of all existing files |
| F6 | Support --dry-run flag to preview what would be copied without copying |
| F7 | Display a live progress line: current filename, N/Total, MB transferred, elapsed time, ETA |
| F8 | Write a final summary: files copied, skipped, failed, total bytes, elapsed time |
| F9 | On per-file error: log the error, skip that file, and continue — never abort the run |
| F10 | Always write a timestamped log file alongside the destination (default: iPhoneCopy_<timestamp>.log) |
| F11 | Support --since <YYYY-MM-DD> to copy only files modified on or after the given date |
| F12 | Support --flat to copy all files into destination root with no subfolder structure |
| F13 | Handle duplicate filenames (same name in different DCIM subfolders) without silently overwriting — append a counter suffix if needed |
| # | Requirement |
|---|---|
| R1 | Resume/checkpoint: Maintain a state file (.iPhoneCopy_state.json) in the destination recording every successfully completed file. On restart, skip files already recorded in the state file, regardless of --overwrite. |
| R2 | Atomic writes: Copy each file to a .tmp temp file first; rename to final name only on successful completion. A partial file from a crash will never silently masquerade as a good copy. |
| R3 | Retry with backoff: On transient USB/WPD errors for a single file, retry up to 3 times with 5-second delays before recording it as failed and moving on. |
| R4 | Post-copy size verification: After each file is copied, verify the destination file size matches the source size. On mismatch, delete the bad copy, record as failed, and continue. |
| R5 | Graceful Ctrl+C handling: On SIGINT/cancel, finish the file currently in progress, flush the state file, write a partial summary, and exit cleanly. The next run will resume from where it stopped. |
| R6 | Streaming transfer: Copy file data in chunks (e.g., 4 MB buffers) — never load an entire file into memory. Essential for large video files. |
| R7 | Memory-bounded enumeration: Do not build a list of all 23,000+ file objects in memory before starting. Enumerate and copy in a pipeline/streaming fashion, or enumerate in batches. |
| R8 | Stale temp file cleanup: On startup, scan the destination for any leftover .tmp files from a previous aborted run and delete them before starting. |
| R9 | USB disconnect detection: If the device disappears mid-run (cable pulled, phone locked, screen timeout), detect the error immediately, log it clearly ("Device disconnected — reconnect and re-run to resume"), and exit with a non-zero code. Do not loop indefinitely. |
| R10 | Screen timeout warning: At startup, remind the user to disable iPhone auto-lock (Settings → Display & Brightness → Auto-Lock → Never) for unattended overnight runs. |
| # | Requirement |
|---|---|
| N1 | Standalone .exe — no .NET runtime or other dependencies required on the target machine |
| N2 | Must not require jailbreak |
| N3 | Requires Apple Mobile Device Support drivers (bundled with iTunes or the Apple Devices app from the Microsoft Store) |
| N4 | Must handle 23,000+ files without memory growth or degradation over time |
| N5 | Executable size < 100 MB |
| N6 | Exit code 0 = all files copied/skipped successfully; 1 = completed with some errors; 2 = fatal error (device not found, destination not writable, etc.) |
Usage:
iPhoneCopy <destination> [options]
Arguments:
destination Local folder to copy files into (created if it does not exist)
Options:
--device <name> Select a specific device by name (if multiple are connected)
--overwrite Re-copy files that already exist at destination (still honors resume state)
--dry-run Preview what would be copied; do not copy or modify anything
--since <date> Only copy files modified on or after <date> (YYYY-MM-DD)
--flat Copy all files into destination root; no DCIM subfolder structure
--log <file> Override the default log file path
--no-resume Ignore the state file and treat this as a fresh run
--retries <n> Number of per-file retries on error (default: 3)
--help Show help
--version Show version
Examples:
iPhoneCopy D:\Photos
iPhoneCopy D:\Photos --since 2025-01-01
iPhoneCopy D:\Photos --dry-run
iPhoneCopy D:\Photos --no-resume --overwrite
The state file <destination>\.iPhoneCopy_state.json is written incrementally after each successful file copy. Format:
{
"device": "iPhone (Alfred's iPhone)",
"started": "2026-03-22T22:00:00Z",
"last_updated": "2026-03-22T23:45:12Z",
"completed": [
{ "src": "DCIM/100APPLE/IMG_0001.HEIC", "dest": "100APPLE\\IMG_0001.HEIC", "size": 3145728, "copied_at": "2026-03-22T22:00:05Z" },
...
]
}On resume, the completed list is loaded into a HashSet<string> keyed on src path for O(1) lookup.
Tab-separated, one line per file, appended in real time:
2026-03-22T22:00:05Z COPIED DCIM/100APPLE/IMG_0001.HEIC -> D:\Photos\100APPLE\IMG_0001.HEIC 3145728 bytes
2026-03-22T22:00:06Z SKIPPED DCIM/100APPLE/IMG_0002.HEIC (already exists, same size)
2026-03-22T22:00:07Z FAILED DCIM/100APPLE/IMG_0003.MOV WPD error after 3 retries: 0x800700AA
2026-03-22T23:45:12Z SUMMARY Copied: 18432 Skipped: 4521 Failed: 12 Bytes: 187,432,904,192 Elapsed: 1h45m12s
Rationale:
- Windows natively exposes the iPhone as a WPD/MTP device when Apple Mobile Device Support drivers are installed (iTunes or the free "Apple Devices" app from the Microsoft Store)
- The WPD COM API (
PortableDeviceApi.dll) is built into Windows — no third-party libraries needed - .NET 8
dotnet publish --self-contained --single-fileproduces a true single.exewith no runtime needed - C# async/await and
Stream-based I/O handle large file streaming cleanly - Strong exception handling model suits the fault-tolerant design
Alternative considered: Python + pymobiledevice3
- AFC protocol, doesn't require iTunes
- Good fallback if WPD proves unreliable for specific iOS versions
- Harder to produce a clean single-file exe on Windows
One of:
- iTunes for Windows (Microsoft Store or apple.com) — installs Apple Mobile Device Support
- Apple Devices app (Microsoft Store) — lightweight, installs the same USB drivers
When the iPhone is first connected, unlock the phone and tap Trust on the "Trust This Computer?" prompt.
Important for overnight runs: Set iPhone → Settings → Display & Brightness → Auto-Lock → Never before starting. Re-enable afterward.
iPhoneCopy/
src/
iPhoneCopy/
Program.cs # Entry point, argument parsing, startup checks
DeviceScanner.cs # WPD device enumeration and selection
FileBrowser.cs # Lazy/streaming DCIM traversal
FileCopier.cs # Atomic copy, retry, size verification
StateFile.cs # Resume checkpoint read/write
ProgressReporter.cs # Live console progress line + ETA
Logger.cs # Append-mode structured log file
ConflictResolver.cs # Duplicate filename handling
iPhoneCopy.sln
PRD.md
README.md
| Milestone | Scope |
|---|---|
| M1 — Device Discovery | Detect and list connected iPhones via WPD; print device name, storage info |
| M2 — DCIM Enumeration | Lazily enumerate all files in DCIM; print names, sizes, dates |
| M3 — Atomic Copy | Copy files with temp-file-then-rename; size verification; error logging |
| M4 — Incremental Skip | Skip by name+size; --overwrite flag; duplicate filename resolution |
| M5 — Resume/Checkpoint | State file: write after each success; resume on restart; --no-resume flag |
| M6 — Retry & Disconnect | Per-file retry with backoff; USB disconnect detection and clean exit |
| M7 — Progress & Logging | Live progress line with ETA; structured log file; final summary |
| M8 — Filtering & Flags | --since, --flat, --dry-run, --device, --log flags |
| M9 — Packaging | Single-file self-contained .exe; exit codes; startup warning for Auto-Lock |