A Comprehensive Guide to Detecting iOS Device Models in Swift

Nov 19, 2025 · Programming · 12 views · 7.8

Keywords: Swift | iOS | Device Model | UIDevice | uname

Abstract: This article provides an in-depth exploration of methods to detect specific iOS device models in Swift, addressing the limitations of UIDevice.model. It includes pure Swift extensions using the uname system call, alternative enum-based approaches, and practical applications for UI adaptations. Code examples are thoroughly explained to facilitate implementation.

Introduction

In iOS app development, accurately retrieving the device model name, such as iPhone 13 Pro or iPad Pro, is essential for feature adaptation, statistical analysis, or UI customization. However, standard APIs like UIDevice.current.model only return generic device types (e.g., "iPhone" or "iPad") without distinguishing specific models. This article addresses this issue using pure Swift methods, leveraging system calls and mapping techniques for precise device model detection.

Limitations of UIDevice.model

The UIDevice.current.model property returns a string describing the broad device category but lacks detailed information. For instance, it may uniformly return "iPhone" for all iPhone models, unable to identify whether it is an iPhone 12 or iPhone 13. This limitation drives developers to seek alternatives, such as using Unix system calls to obtain internal device identifiers.

Using the uname System Call

In iOS, the uname function, derived from Unix systems, retrieves system information including the machine identifier (e.g., "iPhone13,2" for iPhone 12). By mapping this identifier to a human-readable model name, precise device detection can be achieved. The following code demonstrates how to use uname in Swift:

import UIKit

public extension UIDevice {
    static let modelName: String = {
        var systemInfo = utsname()
        uname(&systemInfo)
        let machineMirror = Mirror(reflecting: systemInfo.machine)
        let identifier = machineMirror.children.reduce("") { currentIdentifier, element in
            guard let byteValue = element.value as? Int8, byteValue != 0 else { return currentIdentifier }
            return currentIdentifier + String(UnicodeScalar(UInt8(byteValue)))
        }
        
        func mapIdentifierToModel(deviceIdentifier: String) -> String {
            #if os(iOS)
            switch deviceIdentifier {
            case "iPod5,1": return "iPod touch (5th generation)"
            case "iPhone4,1": return "iPhone 4s"
            case "iPhone5,1", "iPhone5,2": return "iPhone 5"
            case "iPhone13,2": return "iPhone 12"
            case "iPhone14,5": return "iPhone 13"
            case "iPhone15,2": return "iPhone 14 Pro"
            case "iPhone16,1": return "iPhone 15 Pro"
            case "iPad13,4": return "iPad Pro (11-inch) (3rd generation)"
            case "i386", "x86_64", "arm64": 
                let simulatorIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"
                return "Simulator " + mapIdentifierToModel(deviceIdentifier: simulatorIdentifier)
            default: return deviceIdentifier
            }
            #elseif os(tvOS)
            switch deviceIdentifier {
            case "AppleTV5,3": return "Apple TV 4"
            default: return deviceIdentifier
            }
            #else
            return deviceIdentifier
            #endif
        }
        
        return mapIdentifierToModel(deviceIdentifier: identifier)
    }()
}

This extension adds a static property modelName to UIDevice, which returns the detailed model name when called. For real devices, it outputs strings like "iPhone 14 Pro"; for simulators, it outputs "Simulator iPhone 14 Pro". The mapping function covers various device models to ensure compatibility.

Alternative Approach with Enums

Another method involves using enumeration types to represent device models, as seen in reference Answer 2. This approach facilitates conditional checks in code, such as adjusting UI elements based on device type. Here is a simplified enum implementation:

public enum DeviceModel: String {
    case iPhone4 = "iPhone 4"
    case iPhone4S = "iPhone 4s"
    case iPhone5 = "iPhone 5"
    case iPhone13 = "iPhone 13"
    case iPhone14Pro = "iPhone 14 Pro"
    case iPadPro11 = "iPad Pro (11-inch)"
    case simulator = "simulator"
    case unrecognized = "unrecognized"
}

public extension UIDevice {
    var deviceType: DeviceModel {
        var systemInfo = utsname()
        uname(&systemInfo)
        let modelCode = withUnsafePointer(to: &systemInfo.machine) {
            $0.withMemoryRebound(to: CChar.self, capacity: 1) { pointer in
                String(validatingUTF8: pointer)
            }
        }
        
        let modelMapping: [String: DeviceModel] = [
            "iPhone3,1": .iPhone4,
            "iPhone4,1": .iPhone4S,
            "iPhone5,1": .iPhone5,
            "iPhone14,5": .iPhone13,
            "iPhone15,2": .iPhone14Pro,
            "iPad13,4": .iPadPro11,
            "i386": .simulator,
            "x86_64": .simulator
        ]
        
        if let code = modelCode, let model = modelMapping[code] {
            if model == .simulator, let simCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"], let simModel = modelMapping[simCode] {
                return simModel
            }
            return model
        }
        return .unrecognized
    }
}

With enums, developers can retrieve the device type via UIDevice().deviceType and easily handle different cases in switch statements, such as adjusting font sizes or layouts.

Practical Applications

Device model detection is useful in various scenarios. For example, as referenced in the provided article, developers can adjust the user interface based on whether the device has a Face ID sensor. By combining device model information with other properties like safeAreaInsets, more robust adaptations can be achieved. Here is an example demonstrating how to add bottom padding based on device model:

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
                .padding()
        }
        .modifyForNonFaceIDDevices()
    }
}

struct NonFaceIDPaddingModifier: ViewModifier {
    func body(content: Content) -> some View {
        if UIDevice.current.deviceType == .iPhoneSE || UIDevice.current.deviceType == .iPhone5 {
            content.padding(.bottom, 20)
        } else {
            content
        }
    }
}

extension View {
    func modifyForNonFaceIDDevices() -> some View {
        modifier(NonFaceIDPaddingModifier())
    }
}

This code checks if the device is a non-Face ID model (e.g., iPhone SE) and automatically adds bottom padding to avoid UI element overlap with the home indicator. This approach reduces the need for hardcoded device lists and improves code maintainability.

Conclusion

In summary, by using the uname system call and custom mappings, developers can accurately detect iOS device models in Swift. The string extension method is straightforward, while the enum approach offers better type safety and readability. In practical applications, combining device model information with UI properties (e.g., safe areas) can optimize user experience. It is recommended to regularly update mappings to support new devices and refer to official documentation or community resources like iPhoneWiki for the latest identifier information.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.