Fix: `UIDevice.model` Shows Only Device Type in Swift

Fix: `UIDevice.model` Shows Only Device Type in Swift

Written by

in

Quick answer: Use Darwin’s uname function to read the device’s machine identifier, such as iPhone14,5, then map that identifier to a model name. UIDevice.current.model reports a broad type such as iPhone; it does not provide the specific model name.

Why this happens

UIDevice.current.model tells you the general device type, such as iPhone, iPad, or iPod touch. It is not intended to identify a particular generation or marketing name. That is why asking it for an iPhone 13 or iPhone 14 returns only iPhone.

For a more specific value, read the machine identifier exposed by Darwin’s uname function. Identifiers look like iPhone14,5; they are not human-readable model names. To display a name such as “iPhone 13,” your app needs to map known identifiers to names and decide what to do with identifiers it does not recognize.

How to fix it

1. Read the machine identifier with uname

Import Darwin, call uname, and convert the machine field from its C character tuple into a Swift string. This works for iOS device builds and returns an identifier that your code can use for a lookup.

import Darwin

func machineIdentifier() -> String {
    var systemInfo = utsname()

    guard uname(&systemInfo) == 0 else {
        return "Unknown"
    }

    let capacity = MemoryLayout.size(ofValue: systemInfo.machine)

    return withUnsafePointer(to: &systemInfo.machine) { pointer in
        pointer.withMemoryRebound(to: CChar.self, capacity: capacity) {
            String(cString: $0)
        }
    }
}

For example, print the raw value while checking the result on a device:

print(machineIdentifier())

The value is typically an identifier such as iPhone14,5, rather than a friendly name. Keep this raw identifier available in your code: it is a useful fallback when your lookup has not been updated for a newer device.

2. Map identifiers to readable names

Add a lookup for the devices your app needs to recognize. The identifiers below are examples; a production mapping should cover the models relevant to your app and be maintained as new devices appear.

func deviceName(for identifier: String) -> String {
    switch identifier {
    case "iPhone14,5":
        return "iPhone 13"
    case "iPhone14,7":
        return "iPhone 14"
    case "iPhone15,2":
        return "iPhone 14 Pro"
    case "iPhone15,3":
        return "iPhone 14 Pro Max"
    default:
        return identifier
    }
}

Use the two functions together wherever you need a display value:

let identifier = machineIdentifier()
let name = deviceName(for: identifier)

print("Model: \(name)")

Returning the identifier in the default case is usually more useful than returning a generic label. It lets you recognize an unfamiliar device in logs or support reports without pretending that your mapping knows its marketing name. If you prefer not to show raw identifiers to users, return a neutral value such as iPhone instead.

3. Treat Simulator results separately

Do not assume that calling uname in the iOS Simulator will return the identifier of the selected simulated iPhone. Simulator runs your app on the Mac, so the machine value can describe the host architecture rather than the simulated device model. A lookup that expects identifiers such as iPhone14,5 may therefore not match when you run in Simulator.

If your app only needs to adapt its layout or behavior, test the relevant device configurations in Xcode rather than relying on a model-name lookup. If you specifically need the selected Simulator model for a development tool, handle Simulator as a separate case and use a method that is appropriate for your tooling; do not present a host architecture as an iPhone model.

4. Use a maintained mapping when you need broad coverage

A hand-written switch is simple, but it becomes a maintenance task as Apple releases devices. If your app needs names for many iPhone, iPad, iPod touch, or other Apple device models, consider a maintained device-identification package such as DeviceKit. Check its current platform and package-manager support before adding it, and still define a sensible fallback for identifiers the package does not recognize.

Use model detection only when the exact hardware identity matters. For layout, choose SwiftUI layout tools and size classes that respond to available space instead of branching on a particular iPhone generation. Different models can share the same general interface needs, and future models may not be in a fixed list.

How to prevent it

  • Use UIDevice.current.model when a broad type such as iPhone or iPad is sufficient.
  • Use uname when you need the lower-level machine identifier.
  • Keep identifier-to-name mappings separate from the code that reads the identifier.
  • Provide a fallback for unknown identifiers; do not assume the mapping is complete.
  • Avoid using model names to control layout when size classes or available space can express the requirement.
  • Test device-identification code on physical hardware as well as in Simulator, and do not assume Simulator reports the selected device through uname.

FAQ

Can UIDevice.current.model return “iPhone 14”?

No. It returns a general device type, such as iPhone. Read the machine identifier with uname and map that value if you need a specific model name.

Is the value from uname the model name users see?

No. It is a machine identifier, for example iPhone14,5. Your app must translate known identifiers into friendly names, or show the identifier as a fallback.

Why does my device lookup fail in Simulator?

The Simulator runs on your Mac, so uname may describe the host architecture instead of the selected simulated device. Treat Simulator as a separate environment and avoid assuming its machine value is an iPhone identifier.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.