SwiftDeviceKit identifies the iPhone, iPad, Apple Watch, Apple TV, Mac or Apple Vision Pro running your app. Based on DeviceKit, it brings all six device families into one Swift catalog. An iPhone app can read Apple Watch specifications, and a native Mac app can identify its host and read its battery level when available.
Choose SwiftDeviceKitCore for the hardware catalog without UIKit. Choose SwiftDeviceKitSystem when you also need live readings. System includes every Core feature through the same Device type.
SwiftDeviceKit focuses on the hardware modern apps can run on. Its catalog counts 173 models across six Apple device families, each capable of running the package’s minimum OS for its platform or a newer release. For an app targeting iOS 18, Device.allPhones already gives you the compatible iPhones, with older generations filtered out. Minimum OS by platform
In our tests, the SwiftDeviceKit development build identified the Mac running an iPad app, distinguished the 40 mm and 44 mm Apple Watch SE 3 models, and reported the updated free space after a file write. Tested commit 17f99f1
DeviceKit issues marked Open were checked on 8 November 2026. The last column links to the change shipped in SwiftDeviceKit.
| Scenario | DeviceKit 5.10.0 | SwiftDeviceKit 2026.2.0 | Report or evidence | SwiftDeviceKit fix |
|---|---|---|---|---|
| iPad app on a MacBook Pro | ❌ iPad8,6 · iPadOS |
✅ MacBookPro18,3 · macOS |
DeviceKit #506 · Open | 61a13c8 |
| iPhone-only app on an iPad | ❌ Both isPhone and isPad are true |
✅ Only isPad is true |
DeviceKit #447 · Open | 8daaaa9 |
| Apple Pencil on iPad Pro M5 | ❌ Includes Pencil (2nd generation) | ✅ Pencil Pro and USB-C | DeviceKit #504 · Open | a066b86 |
| 11-inch iPad Pro (M4 or M5) screen ratio in portrait | ❌ 605:417 | ✅ 417:605 in portrait | DeviceKit #433 · Open (M4) | 887a59f |
Watch SE 3 case size (Watch7,14 / Watch7,15) |
❌ 40 mm / 44 mm | ✅ 44 mm / 40 mm, matching Apple’s profiles | Measurements | f40aa7e |
| Apple Watch Ultra 2 pixel density | ❌ 338 PPI | ✅ 326 PPI, matching Apple’s specifications | Apple specs | f40aa7e |
| Free space after a 32 MiB write | ❌ Repeated the earlier reading | ✅ Matched a fresh filesystem reading | DeviceKit #525 · Open | da19bc5 |
| Sorting charging and unplugged batteries at 50% | ❌ Unequal states with neither sorting first | ✅ Unplugged sorts before charging | DeviceKit #527 · Open | be91a77 |
| Mac battery in Catalyst | ❌ 0% | ✅ 80%, matching pmset |
Measured values | b05519e |
| Unavailable display brightness in Catalyst | ❌ −100% | ✅ nil when UIKit has no reading |
Measurements | 38f0148 |
| Unknown battery in an iPad app on the Vision simulator | ❌ Full, 100% | ✅ nil for state and level |
Measurements | 17f99f1 |
| Unknown ID with the display name “iPhone Xs” | ❌ Compares equal to the known XS | ✅ Remains a distinct device | DeviceKit #526 · Open | 5f5a6a0 |
| Twice-wrapped iPhone XS simulator | ❌ isPhone is false |
✅ isPhone is true for the underlying XS |
DeviceKit #528 · Open | a6d14b5 |
We also track findings in SwiftDeviceKit’s own issue tracker. Closed SwiftDeviceKit reports and fix commits · Four audit fixes and their retest results.
In Xcode, choose File → Add Package Dependencies… and paste this URL.
https://github.com/Anywhere-Music-Player/SwiftDeviceKit.git
Select the Exact Version dependency rule and enter 2026.2.0. Add one product to your app target. Release notes
Both products require Swift tools 6.4+ and use Swift 6 language mode. Core provides the same 173-model catalog on every platform. System readings depend on the operating system and hardware.
| Device | Minimum OS | Examples of System readings |
|---|---|---|
| iPhone | iOS 18 | Battery, brightness, orientation and Display Zoom |
| iPad | iPadOS 18 | The same readings as iPhone, including in iPhone-only apps |
| Apple Watch | watchOS 11 | Battery, thermal state, storage and display pixels |
| Apple TV | tvOS 18 | Connected display pixels, thermal state and free storage |
| Mac | macOS 15 | Host OS, main display pixels and internal battery when present |
| Apple Vision Pro | visionOS 2 | Battery when available, thermal state, storage and Guided Access |
The platform guide lists model coverage, API availability, Release client sizes and test results for each platform.
| Feature | Core | System |
|---|---|---|
| Model name, device family, processor and simulator detection | ✓ | ✓ |
| SwiftUI preview detection in debug builds | ✓ | ✓ |
| Screen size, pixel density and both iPhone Duo panels | ✓ | ✓ |
| Camera lenses and variable aperture, LiDAR, Dynamic Island and Action button | ✓ | ✓ |
| Face ID, Touch ID and Optic ID hardware | ✓ | ✓ |
| Apple Watch screen resolution and Vision Pro display specifications | ✓ | ✓ |
| Apple Pencil and wireless charging support, USB-C | ✓ | ✓ |
| 5G, eSIM and dual eSIM capabilities | ✓ | ✓ |
OS name and version, isPhone and isPad with iOS idiom fallback |
— | ✓ |
| Battery percentage and charging state on battery-equipped devices | — | ✓ |
| Low Power Mode and thermal state | — | ✓ |
| Available and total storage capacity | — | ✓ |
| Current display pixels, brightness and Display Zoom, orientation and Guided Access where available | — | ✓ |
import SwiftDeviceKitCore
let device = Device.current
print(device.realDevice) // e.g. iPhone 18 Pro
print(device.cpu) // A20 Pro on iPhone 18 Pro
print(device.isSimulator) // true in SimulatorUse realDevice to match the same model on hardware and in Simulator.
import SwiftDeviceKitCore
let model = Device.current.realDevice
if model == .iPhoneDuo {
for screen in model.screens {
print("\(screen.role) display · \(screen.diagonal)″")
}
} else if let screen = model.screen(.main) {
print("\(model) · \(screen.diagonal)″ display")
}On iPhone 18 Pro this prints iPhone 18 Pro · 6.3″ display. On iPhone Duo it prints outer display · 5.4″ and inner display · 7.6″. For the current window size, use your view’s geometry.
import SwiftDeviceKitCore
let model = Device.current
if model.isFaceIDCapable {
print("Face ID")
} else if model.isTouchIDCapable {
print("Touch ID")
} else if model.isOpticIDCapable {
print("Optic ID")
}import SwiftDeviceKitCore
let device = Device.current
print(device.hasTelephotoCamera) // true on iPhone 18 Pro, false on iPhone Duo
print(device.hasVariableAperture) // true on iPhone 18 Proimport SwiftDeviceKitCore
let device = Device.current
if device.isWatch, let pixels = device.screen(.main)?.resolution {
print("\(pixels.width) × \(pixels.height)") // 416 × 496 on Apple Watch Series 12 (46 mm)
} else if let display = device.spatialDisplay {
print(display.refreshRatesHz) // [90, 96, 100, 120] on Apple Vision Pro (M5)
}Import System alone to use these examples. Access UIKit and WatchKit readings on the main actor.
import SwiftDeviceKitSystem
let device = Device.current
if device.isPhone && device.supportsWirelessCharging {
print("This iPhone supports wireless charging.")
}
if device.isPad && device.applePencilSupport.contains(.pro) {
print("This iPad supports Apple Pencil Pro.")
}import SwiftDeviceKitSystem
let device = Device.current
if let name = device.systemName, let version = device.systemVersion {
print("\(device.realDevice) · \(name) \(version)")
}Available on iPhone, iPad, Apple Watch, Mac and Vision Pro. An unavailable battery reading returns nil, including on a desktop Mac without an internal battery. Designed for iPad/iPhone apps on Mac report battery readings as unavailable. Mac Catalyst reads the internal battery through IOKit. iPhone, iPad and Watch simulators retain the full-battery fallback for an unknown state.
import SwiftDeviceKitSystem
#if !os(tvOS)
@MainActor
func batteryLabel() -> String {
switch Device.current.batteryState {
case .charging(let percent): "\(percent)% · Charging"
#if os(macOS) || targetEnvironment(macCatalyst)
case .pluggedInNotCharging(let percent): "\(percent)% · Connected to power"
#endif
case .unplugged(let percent): "\(percent)%"
case .full: "Charged"
case nil: "Battery unavailable"
}
}
#endifUse Device.current.batteryLevel for the measured percentage. On Mac, IOKit can report a plugged-in battery as charged at 95–99%.
import SwiftDeviceKitSystem
@MainActor
func shouldDeferBackgroundWork() -> Bool {
let device = Device.current
if device.isLowPowerModeEnabled == true { return true }
switch device.thermalState {
case .serious, .critical: return true
default: return false
}
}import SwiftDeviceKitSystem
func hasSpaceForDownload(bytes: Int64) -> Bool? {
Device.volumeAvailableCapacity.map { Int64($0) >= bytes }
}On watchOS both capacity properties return Int64?, including on arm64_32 watches. On the other platforms their return type is Int?. For a user-requested download on iPhone, iPad, Mac or Vision Pro, volumeAvailableCapacityForImportantUsage also accounts for space the OS can reclaim.
import SwiftDeviceKitSystem
#if !os(visionOS)
let device = Device.current
if let pixels = device.currentDisplayPixels {
print("\(pixels.width) × \(pixels.height)") // Connected display on Apple TV
}
#if os(iOS)
if let brightness = device.screenBrightness {
print("Brightness: \(brightness)%")
}
if device.isZoomed == true {
print("Display Zoom is enabled.")
}
#endif
#endifTo read physical orientation on iPhone or iPad, call Device.beginOrientationUpdates(),
read Device.current.orientation after a device orientation notification, and call
Device.endOrientationUpdates() when finished. Orientation lifecycle
- Platform support and verification
- Migrating from DeviceKit
- Screen specifications
- Adding a model and testing the catalog
- Changelog
SwiftDeviceKit is distributed under the MIT license. See attribution for the original authors and sources.