A dependency-free Swift package for talking to Android devices from macOS — browse storage and move files both ways over USB(MTP) and Wi-Fi(ADB), behind one async DeviceTransport abstraction.
- MTP over USB, spoken directly through
IOUSBHost— nolibmtp, no C dependencies. - ADB over Wi-Fi transport, with QR/pairing-code/IP pairing and mDNS auto-discovery.
- A single
DeviceTransportprotocol for both backends, plus aMockTransportfor tests and previews. async/awaitthroughout,Sendablevalue types, and a real-timeDeviceChangeevent stream.- Streaming up/download with progress reporting; handles files larger than 4 GB.
- Zero third-party dependencies. Error messages localized in English and 繁體中文.
- macOS 15+
- Swift 6+(Xcode 16+)
Swift Package Manager:
.package(url: "https://github.com/5j54d93/MTPKit.git", from: "0.1.0")Then add "MTPKit" to your target's dependencies.
import MTPKit
// USB/MTP
guard let transport = await MTPTransport.discover() else { return }
let storages = try await transport.storages()
let root = try await transport.listChildren(of: nil, in: storages[0].id)
try await transport.download(node.id, to: localURL) { progress in
print(progress.fractionCompleted)
}Both USB and Wi-Fi backends conform to DeviceTransport, so they're interchangeable from the caller's side.
adbbinary(Wi-Fi only): MTPKit does not bundleadb.ADBClientlooks for it first in your app bundle(Bundle.main), then in common install locations(Homebrew、Android SDK), or you can pass an explicit path withADBClient(adbPath:). The USB/MTP path needs nothing extra.- Entitlements: USB access via
IOUSBHostrequires the consuming app to declare the appropriate USB device entitlement, and the ADB transport spawns theadbsubprocess — configure your app's sandbox/entitlements accordingly. A library can't carry these for you. - Tests: the hardware("live")tests no-op gracefully when no device is attached, so the suite stays green on CI/without a phone.
This package is MIT licensed.