Back to projects
active v0.2.1

MacPhone

A native macOS device lab that bridges a real Bluetooth LE device into an emulator.

MacPhone runs and controls many Android emulators and iOS simulators from one Mac, and bridges a real Bluetooth LE device straight into an emulator — mirroring its full GATT tree onto the emulator's virtual controller so on-device apps see the real services, characteristics, and advertisement with no dongle.

MacPhone overview dashboard listing connected Android emulators and iOS simulators
Problem

The hard part of testing a hardware-talking mobile app isn't the app — it's getting a real BLE device in front of code running inside an emulator.

What I built

A SwiftUI app that manages AVDs and Xcode simulators, connects to a physical BLE device over CoreBluetooth, mirrors its full GATT tree, and re-broadcasts it on the Android emulator's netsim controller via a Bumble virtual peripheral, forwarding reads, writes, and notifications both ways.

Result

An emulated app under test sees the real device's services and live traffic, and sessions tear down cleanly with no orphaned peripherals.

Audience

For mobile developers testing hardware-talking apps without juggling physical devices and BLE dongles.

Developer setup

source without GitHub CLI git clone https://github.com/jx-grxf/MacPhone.git && cd MacPhone
source with GitHub CLI gh repo clone jx-grxf/MacPhone && cd MacPhone
latest release open https://github.com/jx-grxf/MacPhone/releases/tag/v0.2.1
download stable macos with GitHub CLI gh release download v0.2.1 -R jx-grxf/MacPhone -p 'MacPhone-0.2.1.dmg'
download stable archive with GitHub CLI gh release download v0.2.1 -R jx-grxf/MacPhone -p 'MacPhone-0.2.1.zip'

Highlights

  • Manages Android emulators (AVDs) and Xcode iOS simulators in parallel from one panel.
  • Bridges a real BLE device into the emulator over CoreBluetooth, mirroring the full GATT tree.
  • Forwards reads, writes, and notifications both ways — the app under test sees the real hardware.
  • Keeps sessions clean: disconnects stale clients and tears the bridge down with its parent process.