Human-readable iPhone model names

Hello Swift community,

I’d like to share a small but useful library I’ve been maintaining: iModels. It provides a simple way to get human-readable device model names (e.g., iPhone 6s Plus) from Apple’s internal identifiers (e.g., iPhone8,2). This can be especially handy for analytics, debugging, or tailoring app behavior based on device families.

Key Features

  • :mobile_phone: Converts Apple’s device identifiers into human-readable names.

  • :desktop_computer: Works seamlessly in both device and simulator contexts (adds “Simulator” suffix when appropriate).

  • :wrench: Supports Swift Package Manager and CocoaPods (though future releases will focus on SPM).

  • :white_check_mark: Includes unit tests to ensure correctness and reliability.

Usage

import iModels

print(Device.modelName)
// e.g. "iPhone 6s Plus" or "iPhone 14 Pro Max Simulator"

print(Device.identifier)
// e.g. "iPhone8,2"

print(Device.modelName(for: "iPhone8,2") ?? "unknown")
// Output: iPhone 6s Plus

Motivation

Apple’s identifiers are not intuitive, and developers often need a quick way to map them to actual device names. iModels aims to simplify this process with a lightweight, dependency-free solution.

Links

5 Likes

Hey @AnbalaganD , how do you manage to keep this upto date

There's actually undocumented-but-not-private API for this on macOS based on UTTypes. I mentioned it in a WWDC talk I gave a few years back. :smiling_face_with_horns:

3 Likes

Thank you Jonathan,

Potential usage example:

import UniformTypeIdentifiers

var deviceModelName: String? {
    var suffix = ""
    
    #if os(macOS)
        let supertype = "com.apple.mac"
    #elseif os(iOS)
        let supertype = "com.apple.phone"
    #else
        #error("TODO")
    #endif
    
    #if targetEnvironment(simulator)
        guard let machine = ProcessInfo.processInfo.environment["SIMULATOR_MODEL_IDENTIFIER"] else {
            return nil
        }
        suffix = " (Simulator)"
    #elseif os(macOS)
        var size = 0
        sysctlbyname("hw.model", nil, &size, nil, 0)
        var buf = [CChar](repeating: 0, count: size)
        sysctlbyname("hw.model", &buf, &size, nil, 0)
        let machine = String(cString: buf)
    #elseif os(iOS)
        var name = utsname()
        uname(&name)
        let machine = withUnsafePointer(to: &name.machine) {
            $0.withMemoryRebound(to: CChar.self, capacity: 1) { String(cString: $0) }
        }
    #else
        #error("TODO")
    #endif

    let type = UTType(tag: machine, tagClass: .init(rawValue: "com.apple.device-model-code"), conformingTo: .init(supertype))
    guard let name = type?.localizedDescription else {
        return nil
    }
    return name + suffix
}

I found that on macOS it might return a mere "MacBook Pro", although the underlying UTType's identifier has more to reveal (albeit in a not so human readable form), e.g. "com.apple.macbookpro-16-2021", which is not ideal (but still better than a cryptic machine name which in this case is "MacBookPro18,1").

On iOS it works great out of the box, returning e.g. "iPhone 17 Pro Max".

6 Likes

Uniform Type Identifiers are not meant to be human-readable; they're always in reverse-DNS format.

For Mac models, it's expected that UTType will produce "MacBook Pro" rather than e.g. "MacBook Pro (2028, toasterOS-compatible)" or some other descriptive string. But, if you run into a model that isn't covered by a more bespoke database, that may still do as a fallback in a pinch. :smiling_face_with_sunglasses: