iOS Simulator on Windows: macOS Sonoma and Xcode in VMware on an AMD Ryzen PC
October 26, 2026
If you build iOS apps but your computer runs Windows, the usual advice is "buy a Mac". This guide shows another way that I actually got working: macOS Sonoma with Xcode and the iOS Simulator, running in a VMware virtual machine on a Windows PC with an AMD Ryzen CPU. At the end, an iPhone 15 running iOS 17.5 opens in a window you can click, and you can build and test with Xcode from the command line over SSH.
Everything below was done on one machine, in one long session, including the dead ends. I point out what I verified and what I did not.
Before you start: the honest warnings
- Licensing. Apple's macOS license only allows macOS on Apple hardware. Running it in a VM on a PC goes against that license. Treat this as a personal experiment, and decide for yourself.
- Unsupported. VMware does not support macOS guests on AMD CPUs. This guide relies on a community patch ("unlocker") and on changing the CPU information the VM reports. A VMware update can break it.
- No GPU. A VM on Windows cannot use your graphics card for macOS. The Simulator draws on the CPU. It is fine for normal app screens and slow for games or heavy 3D.
- Windows features you lose. VMware needs the Windows hypervisor turned off. That breaks Docker Desktop, WSL2 and Hyper-V virtual machines until you turn it back on. (A workaround for Docker is at the end.)
- It takes hours. Mostly downloads and one tricky CPU setting.
What you need
- A Windows 11 PC with at least 16 GB RAM (I gave the VM 12 GB) and about 100 GB of free SSD space. Keep the VM on an SSD, not a hard disk. I only tested an AMD Ryzen 5 9600X; other CPUs may need different CPU settings.
- VMware Workstation (free for personal use, I used version 26).
- A free Apple Account, for downloading Xcode from developer.apple.com.
- About 40 GB of downloads: the macOS installer (about 14 GB), Xcode (about 3.4 GB) and the iOS Simulator runtime (about 7 GB).
My machine: Windows 11 Pro, AMD Ryzen 5 9600X, 32 GB RAM, NVMe SSD.
Which versions to choose (this matters)
I tried macOS Sequoia 15 first. Everything booted, but Xcode 15.4's Simulator app crashes on Sequoia with Symbol not found: _OBJC_CLASS_$__GCControllerManager. The simulator itself runs, but you get no window. The fix was macOS Sonoma 14 with Xcode 15.4, which is the combination Xcode 15.4 was built for.
| Piece | Version used |
|---|---|
| macOS | Sonoma 14.8.9 |
| Xcode | 15.4 (build 15F31d) |
| Simulator runtime | iOS 17.5 |
| VM | 6 vCPU, 12 GB RAM, 150 GB growable disk |
If you need a newer Xcode, you need a newer macOS, and the CPU settings below may need changes.
Step 1: Prepare Windows
Turn off the Windows hypervisor and virtualization-based security, and keep a way back.
- In an administrator terminal, back up and change the boot setting:
bcdedit /export C:\backup\bcd-before.bak
bcdedit /set hypervisorlaunchtype off- Turn off Memory integrity in Windows Security, Device security, Core isolation. If it is greyed out or comes back, also set
EnableVirtualizationBasedSecurityto0underHKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard(export the key first). - Reboot, then try to start a VM in VMware. If macOS VMs start slowly or refuse to run, the hypervisor is probably still on.
To undo everything later: bcdedit /set hypervisorlaunchtype auto, restore the registry export, reboot.
Step 2: Install VMware and the unlocker
- Install VMware Workstation.
- Download the unlocker (I used the BDisp fork, "macOS Unlocker V3.0 for VMware Workstation", from its GitHub releases page). It patches VMware so you can pick macOS as the guest and so macOS can boot.
- Close VMware, then run the installer script (
win-install.cmd) as administrator.
Two rules from its own README: never delete the unlocker's backup folder before uninstalling, and always uninstall an old unlocker before installing a new one. Any VMware update undoes the patch, so reinstall it afterwards.
Step 3: Get the macOS installer from Apple
Apple's software update catalog lists every full macOS installer. This PowerShell script downloads the catalog and prints the InstallAssistant.pkg links with their versions. Everything comes from Apple's own servers.
$u = 'https://swscan.apple.com/content/catalogs/others/index-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz'
curl.exe -sSL -o cat.gz $u
$in = [IO.File]::OpenRead("$PWD\cat.gz")
$gz = New-Object IO.Compression.GZipStream($in, [IO.Compression.CompressionMode]::Decompress)
$out = [IO.File]::Create("$PWD\cat.xml"); $gz.CopyTo($out); $out.Close(); $in.Close()
$t = [IO.File]::ReadAllText("$PWD\cat.xml")
$blocks = [regex]::Split($t, '(?=<key>\d{3}-\d{5}</key>\s*<dict>)')
foreach ($b in $blocks) {
$m = [regex]::Match($b, '<string>(https://swcdn\.apple\.com/content/downloads/[^<]*?/InstallAssistant\.pkg)</string>')
if (-not $m.Success) { continue }
$dist = [regex]::Match($b, '(https://[^<]+\.English\.dist)').Groups[1].Value
$x = curl.exe -sSL $dist
$v = [regex]::Match($x, '<key>VERSION</key>\s*<string>([^<]+)</string>').Groups[1].Value
"$v $($m.Groups[1].Value)"
}Pick the Sonoma 14.x line and download it with a download manager. Then check it is complete and genuine:
# the size on your disk must equal the server's Content-Length
curl -sIL <the InstallAssistant.pkg URL> | grep -i content-lengthLater, inside macOS, pkgutil --check-signature InstallAssistant.pkg must say "signed Apple Software".
Step 4: Get a macOS Recovery disk
The first boot needs something to start from. OpenCore's macrecovery script downloads Apple's recovery image from Apple's servers (pick the board ID line for the macOS you want from its README, and choose the macOS version you are installing):
python macrecovery.py -b <BOARD_ID> -m <MLB> downloadYou get BaseSystem.dmg and BaseSystem.chunklist. Convert it into a VMware disk with qemu-img:
qemu-img convert -f dmg -O vmdk BaseSystem.dmg BaseSystem.vmdkStep 5: Create the VM and the CPU settings that make it boot
Create a new VM in VMware (guest OS: Apple macOS), but do not start it yet. Close VMware and edit the .vmx file. This is the full set I ended up with:
.encoding = "windows-1252"
config.version = "8"
virtualHW.version = "21"
displayName = "macOS Sonoma"
guestOS = "darwin24-64"
firmware = "efi"
uefi.secureBoot.enabled = "FALSE"
numvcpus = "6"
cpuid.coresPerSocket = "6"
memsize = "12288"
pciBridge0.present = "TRUE"
pciBridge4.present = "TRUE"
pciBridge4.virtualDev = "pcieRootPort"
pciBridge4.functions = "8"
pciBridge5.present = "TRUE"
pciBridge5.virtualDev = "pcieRootPort"
pciBridge5.functions = "8"
pciBridge6.present = "TRUE"
pciBridge6.virtualDev = "pcieRootPort"
pciBridge6.functions = "8"
pciBridge7.present = "TRUE"
pciBridge7.virtualDev = "pcieRootPort"
pciBridge7.functions = "8"
sata0.present = "TRUE"
sata0:0.present = "TRUE"
sata0:0.fileName = "BaseSystem.vmdk"
sata0:1.present = "TRUE"
sata0:1.fileName = "macOS.vmdk"
ethernet0.present = "TRUE"
ethernet0.connectionType = "nat"
ethernet0.virtualDev = "vmxnet3"
usb.present = "TRUE"
usb_xhci.present = "TRUE"
ehci.present = "TRUE"
mks.enable3d = "FALSE"
svga.vramSize = "268435456"
smc.present = "TRUE"
smc.version = "0"
hw.model.reflectHost = "FALSE"
hw.model = "iMac20,1"
board-id.reflectHost = "FALSE"
board-id = "Mac-AA95B1DDAB278B95"Create the empty 150 GB system disk with vmware-vdiskmanager.exe -c -s 150GB -a sata -t 0 macOS.vmdk (it grows as you use it).
The CPU block (the hard part)
macOS expects an Intel CPU. On an AMD host you make the VM report an Intel one. Add these lines, which are what made Sonoma 14.8.9 boot:
cpuid.0.ebx = "0111:0101:0110:1110:0110:0101:0100:0111"
cpuid.0.ecx = "0110:1100:0110:0101:0111:0100:0110:1110"
cpuid.0.edx = "0100:1001:0110:0101:0110:1110:0110:1001"
cpuid.0.eax = "0000:0000:0000:0000:0000:0000:0001:0110"
cpuid.1.eax = "0000:0000:0000:0011:0000:0110:1100:0011"
cpuid.1.ebx = "0000:0010:0000:0001:0000:1000:0000:0000"
cpuid.1.ecx = "10--:--10:1-01:1000:001-:0010:0000:0011"
cpuid.1.edx = "0000:0111:1000:1011:1111:1011:1111:1111"
cpuid.4.eax = "0000:0000:0000:0000:0000:0001:0010:0001"
cpuid.4.ebx = "0000:0001:1100:0000:0000:0000:0011:1111"
cpuid.4.ecx = "0000:0000:0000:0000:0000:0000:0011:1111"
cpuid.4.edx = "0000:0000:0000:0000:0000:0000:0000:0000"
cpuid.7.ebx = "00-0:00--:--0-:--00:----:----:----:----"
cpuid.7.ecx = "----:----:----:----:-0-0:0---:-0--:--0-"
cpuid.7.edx = "----:----:0---:----:----:---0:----:00--"
cpuid.15.eax = "0000:0000:0000:0000:0000:0000:0000:0001"
cpuid.15.ebx = "0000:0000:0000:0000:0000:0000:0010:0111"
cpuid.15.ecx = "0000:0101:1111:0101:1110:0001:0000:0000"
cpuid.15.edx = "0000:0000:0000:0000:0000:0000:0000:0000"
cpuid.16.eax = "0000:0000:0000:0000:0000:1111:0011:1100"
cpuid.16.ebx = "0000:0000:0000:0000:0000:1111:0011:1100"
cpuid.16.ecx = "0000:0000:0000:0000:0000:0000:0110:0100"
cpuid.16.edx = "0000:0000:0000:0000:0000:0000:0000:0000"What these do:
cpuid.0.*makes the vendor stringGenuineIntel.cpuid.1.eaxreports a Haswell-generation CPU (family 6, model 0x3C). This matters, see below.cpuid.1.ecxlets AVX through. Thecpuid.7.*lines keep AVX2 but hide AVX-512.cpuid.4.*gives a normal cache size (without it the log says "no linesize").cpuid.15andcpuid.16tell the Intel kernel the CPU's clock speed, which it otherwise reads from a register that does not exist on AMD. Without them, a Haswell model crashes with a divide error.
Adjust the clock values for your CPU. cpuid.15.ecx is a 100 MHz base clock (100000000), and cpuid.15.ebx is your CPU's TSC frequency divided by 100 MHz. My TSC was 3.89 GHz, so 39. VMware prints your TSC in its vmware.log ("TSC Hz"). Put that same number in MHz into cpuid.16.eax and cpuid.16.ebx (3900 for me).
Why this was hard (what I learned)
The error you will hit first, on both Sequoia and Sonoma, is this boot panic:
launchd: Library not loaded: /usr/lib/libSystem.B.dylib ... no dyld cache
panic: initproc failed to startIt looks like a corrupt install, but it is not. Recent macOS versions ship their shared library cache only for Haswell-class CPUs (x86_64h). If the kernel thinks the CPU is older, it looks for a cache that does not exist and dies before the system starts.
- On Sequoia it was enough to keep an older Intel model but let AVX through (
hw.cpusubtypebecame 8). - On Sonoma the same settings did not work. The VM log showed the guest did receive AVX2, but the Sonoma kernel ignored it unless the CPU model looked like Haswell, and a Haswell model needs the clock values above.
You can check what the guest sees from Recovery's Terminal. On a working setup:
sysctl hw.cpusubtype hw.optional.avx2_0should print hw.cpusubtype: 8 and hw.optional.avx2_0: 1. If you see 4 and 0, the guest still looks like an old CPU.
How to read the real error
Do not guess. Send the guest's kernel messages to a text file. Add these lines to the .vmx:
serial0.present = "TRUE"
serial0.fileType = "file"
serial0.fileName = "C:\macOS-VM\serial.log"and set verbose boot from Recovery's Terminal: nvram boot-args="-v serial=3". The panic text then appears in serial.log. Remove both when everything works.
Step 6: Install macOS
Boot the VM. If it shows a boot menu, choose the SATA disk that holds BaseSystem.vmdk. You land in macOS Recovery.
- Open Disk Utility and erase the empty disk as APFS, named
Macintosh HD. - Install macOS onto it.
There are two ways to get the very first macOS onto the empty disk:
- Internet install (the simplest, but I did not try it in the final setup): choose "Reinstall macOS" in Recovery and let Apple's servers deliver the installer. It needs a working internet connection inside the VM.
- From the installer you downloaded (what I did for my first macOS): put the installer on a second virtual disk so the VM can read it, expand the package, and run the
startosinstalltool from the installer app against your empty disk. I do not give exact commands here, because this part took the most trial and error and my notes for it are incomplete. The tool lives inside the installer app (Contents/Resources/startosinstall) and prints its options with--help.
Either way, once you have any macOS booting, there is a cleaner route to the exact version you want:
Build a Sonoma installer inside a working macOS
I first got Sequoia running with the CPU settings above, then used it to build a clean Sonoma install disk. Inside the working macOS:
sudo installer -pkg Sonoma_InstallAssistant.pkg -target /
hdiutil create -size 18g -layout GPTSPUD -fs 'HFS+J' -volname SonomaInstall -type UDIF media.dmg
hdiutil attach media.dmg
sudo "/Applications/Install macOS Sonoma.app/Contents/Resources/createinstallmedia" \
--volume /Volumes/SonomaInstall --nointeraction
hdiutil detach "/Volumes/Install macOS Sonoma"
hdiutil convert media.dmg -format UDTO -o media_rawYou now have media_raw.cdr, a raw bootable installer disk. Attach a second empty 20 GB virtual disk to that VM (shut it down, edit the .vmx, boot again), find it with diskutil list, and write the installer onto it:
sudo dd if=media_raw.cdr of=/dev/rdisk3 bs=4m(Use your own disk number. Choosing the wrong one erases it, so check the size.) Shut the VM down. The new virtual disk file on Windows is now a bootable Sonoma installer. Make a new VM with the settings from Step 5, attach that disk plus a fresh empty 150 GB disk, boot from the installer disk, run Install macOS Sonoma, and select the empty disk. The VM restarts a few times. When it reaches the setup screens you are done with the installer.
Finish Setup Assistant yourself and create your user account. Do not share that password anywhere.
Step 7: Turn on SSH
Working in the macOS Terminal is far easier over SSH than through the VM window. In the VM's Terminal:
sudo launchctl load -w /System/Library/LaunchDaemons/ssh.plist
mkdir -p ~/.ssh && chmod 700 ~/.sshOn Windows, create a key (ssh-keygen -t ed25519) and append the contents of the .pub file to ~/.ssh/authorized_keys in the VM. Then from Windows, using the VM's IP address (ifconfig in macOS):
ssh -i C:\macOS-VM\ssh\id_macvm your-user@192.168.130.129Step 8: Install Xcode and the iOS Simulator
Download from developer.apple.com/download/all (a free Apple Account is enough) the files Xcode_15.4.xip and iOS_17.5_Simulator_Runtime.dmg. Get them onto the VM (Step 9 shows a fast way). Then verify them before installing. A genuine Apple file passes these checks, and a tampered copy will not:
pkgutil --check-signature Xcode_15.4.xip # must show "signed Apple Software"
xip --expand Xcode_15.4.xip # takes about 10 minutes
codesign --verify --deep --strict Xcode.app && echo CODESIGN_OK
spctl --assess --type execute -v Xcode.app # must print: accepted, source=Apple SystemThen install and set up:
mv Xcode.app /Applications/
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -license accept
sudo xcodebuild -runFirstLaunch
xcrun simctl runtime add iOS_17.5_Simulator_Runtime.dmg
xcodebuild -versionNote that xcodebuild -importPlatform does not exist in Xcode 15.4. Use simctl runtime add.
Create and start an iPhone, and open the Simulator window:
UDID=$(xcrun simctl create "iPhone 15" com.apple.CoreSimulator.SimDeviceType.iPhone-15 com.apple.CoreSimulator.SimRuntime.iOS-17-5)
xcrun simctl boot $UDID
open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.appThe first start shows "Verifying iOS 17.5.simruntime" for a minute. Then the home screen appears. You can take screenshots with xcrun simctl io $UDID screenshot shot.png, install apps with simctl install, and run tests with xcodebuild test, all over SSH.
Step 9: Move big files into the VM quickly
If copying files over the network is slow, there are two causes worth checking.
The proxy trap. If you use a proxy program on Windows (I used Proxifier with a SOCKS proxy), its default rule may send traffic to your VMs through the proxy too. Most proxies cannot reach private addresses like 192.168.x.x, so SSH to the VM failed with "Connection refused" about half the time, and VM copies crawled at about 20 KB/s. I proved it by asking the proxy directly: public addresses worked, the VM address failed. The fix is one rule above the default one:
Name: Local networks direct
Targets: 192.168.0.0/16; 10.0.0.0/8; 172.16.0.0/12
Action: DirectAfter that, 20 out of 20 SSH tests succeeded. I wasted hours before finding this.
A virtual disk instead of the network. For a huge one-off file, create a blank virtual disk on Windows, attach it to the VM, and write or copy the data on the VM side. This avoids network speed entirely (Step 6 used this for a 19 GB image, which took under a minute).
Step 10: Snapshot and clean up
- Remove the debug lines (
serial0.*, theboot-argswithsudo nvram -d boot-args). - Delete installer files you no longer need.
- Shut the VM down and take a VMware snapshot, so you can always return to a working state.
Performance notes
Without a GPU, the Simulator is smooth for normal screens and slow for animation-heavy apps. Give the VM at least 8 GB of RAM (I used 12) and keep it on an SSD.
Troubleshooting cheat sheet
| What you see | What it means | Fix |
|---|---|---|
Library not loaded: libSystem.B.dylib, panic loop | macOS thinks the CPU is too old for its shared cache | Fix the CPU block, check hw.cpusubtype is 8 |
| Divide error right after changing the CPU model | Missing clock values for a Haswell model | Add cpuid.15 and cpuid.16 |
no linesize in the kernel log | No cache info in the CPU block | Add cpuid.4.* |
| "No PCIe slot available for Ethernet0" | The .vmx lacks PCI bridge entries | Add the pciBridge* lines |
| "Unable to find a slot for PCI bridge" | Old pciSlotNumber lines clash | Delete all pciSlotNumber lines and restart |
| Simulator app quits instantly on Sequoia | Xcode 15.4 needs Sonoma | Use macOS Sonoma 14 |
| SSH "Connection refused" sometimes | A host proxy catches traffic to the VM | Add the "Local networks direct" rule |
| Docker Desktop stopped working | The hypervisor must stay off for macOS | See below |
Bonus: keeping Docker working
Because the Windows hypervisor has to stay off, I run Docker inside a small Ubuntu Server VM in the same VMware, and point the Windows docker command at it:
docker context create docker-vm --docker "host=ssh://docker-vm"
docker --context docker-vm psTwo things to know about a remote Docker engine: bind mounts in compose files are resolved on the VM, not on Windows, so the project needs to live on the VM; and move code with git, not by copying files, because Windows checkouts often have CRLF line endings that break shell scripts inside Linux containers.
Recap
- Turn off the Windows hypervisor, install VMware and the unlocker.
- Download the macOS Sonoma installer from Apple and check its size.
- Create the VM with the Intel-looking Haswell CPU block and your own clock values.
- Install macOS, finish setup yourself, and enable SSH.
- Install Xcode 15.4 and the iOS 17.5 runtime, verifying Apple's signatures first.
- Boot an iPhone simulator and open its window.
- Exclude your VM network from any proxy, and snapshot the result.
If you try this, the single most useful habit is to read the real kernel log before changing settings. It turned hours of guessing into one clear error message.