実行端末の種類を判定する

はじめに

iOS開発をしているとiPhoneSEなどの小さい端末だけどうしてもハードコーディングをして対応しなくてはいけない局面に立ち会うことが多々あります。

今回は端末を判定する方法を調べてみました。

UIDeviceで取れるんじゃないかと思っていたんですが、UIDeviceで取れるのはiPhoneかiPadかなどの大きな範囲での判定しか出来ず、判定するにはピクセルサイズで判定するかutsnameでカーネル情報を取得する必要があります。

カーネル情報を取得して判定する

【Swift2】iOSの端末モデル名を取得するという記事がすごく参考になりました。

だいぶ古い記事だったので最新のSwift5で動くように直しました。(新しく発売された端末も追加)

private let DeviceList = [
    /* -------------- iPod Touch -------------- */
    /* iPod Touch 5 */    "iPod5,1": "iPod Touch 5",
    /* iPod Touch 6 */    "iPod7,1": "iPod Touch 6",

    /* -------------- iPhone -------------- */
    /* iPhone 4 */        "iPhone3,1": "iPhone 4", "iPhone3,2": "iPhone 4", "iPhone3,3": "iPhone 4",
    /* iPhone 4S */       "iPhone4,1": "iPhone 4S",
    /* iPhone 5 */        "iPhone5,1": "iPhone 5", "iPhone5,2": "iPhone 5",
    /* iPhone 5C */       "iPhone5,3": "iPhone 5C", "iPhone5,4": "iPhone 5C",
    /* iPhone 5S */       "iPhone6,1": "iPhone 5S", "iPhone6,2": "iPhone 5S",
    /* iPhone 6 */        "iPhone7,2": "iPhone 6",
    /* iPhone 6 Plus */   "iPhone7,1": "iPhone 6 Plus",
    /* iPhone 6S */       "iPhone8,1": "iPhone 6S",
    /* iPhone 6S Plus */  "iPhone8,2": "iPhone 6S Plus",
    /* iPhone SE */       "iPhone8,4": "iPhone SE",
    /* iPhone 7 */        "iPhone9,1": "iPhone 7", "iPhone9,3": "iPhone 7",
    /* iPhone 7 Plus */   "iPhone9,2": "iPhone 7 Plus", "iPhone9,4": "iPhone 7 Plus",
    /* iPhone 8 */        "iPhone10,1": "iPhone 8", "iPhone10,4": "iPhone 8",
    /* iPhone 8 Plus */   "iPhone10,2": "iPhone 8 Plus", "iPhone10,5": "iPhone 8 Plus",
    /* iPhone X */        "iPhone10,3 ": "iPhone X", "iPhone10,6": "iPhone X",
    /* iPhone XR */       "iPhone11,8": "iPhone XR",
    /* iPhone XS */       "iPhone11,2": "iPhone XS",
    /* iPhone XS Max */   "iPhone11,6": "iPhone XS Max",

    /* -------------- iPad -------------- */
    /* iPad 2 */          "iPad2,1": "iPad 2", "iPad2,2": "iPad 2", "iPad2,3": "iPad 2", "iPad2,4": "iPad 2",
    /* iPad 3 */          "iPad3,1": "iPad 3", "iPad3,2": "iPad 3", "iPad3,3": "iPad 3",
    /* iPad 4 */          "iPad3,4": "iPad 4", "iPad3,5": "iPad 4", "iPad3,6": "iPad 4",
    /* iPad Air */        "iPad4,1": "iPad Air", "iPad4,2": "iPad Air", "iPad4,3": "iPad Air",
    /* iPad Air 2 */      "iPad5,3": "iPad Air 2", "iPad5,4": "iPad Air 2",
    /* iPad Mini */       "iPad2,5": "iPad Mini", "iPad2,6": "iPad Mini", "iPad2,7": "iPad Mini",
    /* iPad Mini 2 */     "iPad4,4": "iPad Mini 2", "iPad4,5": "iPad Mini 2", "iPad4,6": "iPad Mini 2",
    /* iPad Mini 3 */     "iPad4,7": "iPad Mini 3", "iPad4,8": "iPad Mini 3", "iPad4,9": "iPad Mini 3",
    /* iPad Mini 4 */     "iPad5,1": "iPad Mini 4", "iPad5,2": "iPad Mini 4",
    /* iPad Pro(12.9) */  "iPad6,7": "iPad Pro", "iPad6,8": "iPad Pro",
    /* iPad Pro (9.7) */  "iPad6,3": "iPad Pro (9.7)", "iPad6,4": "iPad Pro (9.7)",
    /* iPad (5th) */      "iPad6,11": "iPad (5th)",  "iPad6,12": "iPad (5th)",
    /* iPad Pro2 (12.9)*/ "iPad7,1": "iPad Pro2 (12.9)", "iPad7,2": "iPad Pro2 (12.9)",
    /*iPad Pro (10.5) */  "iPad7,3": "iPad Pro (10.5)", "iPad7,4": "iPad Pro (10.5)",
    /*iPad (6th)*/        "iPad7,5": "iPad (6th)",  "iPad7,6": "iPad (6th)",
    /* Simulator */       "x86_64": "Simulator", "i386": "Simulator",
]

extension UIDevice {

    var platform: String {
        var systemInfo = utsname()
        uname(&systemInfo)

        let mirror = Mirror(reflecting: systemInfo.machine)
        var identifier = ""

        for child in mirror.children {
            if let value = child.value as? Int8, value != 0 {
                identifier.append(UnicodeScalar(UInt8(bitPattern: value)).description)
            }
        }
        return identifier
    }

    var modelName: String {
        let identifier = platform
        return DeviceList[identifier] ?? identifier
    }
}

このコードを最初にみたときは一体何をしているのか全くわからなかったのですが、よくよく調べてみるとutsnameというのはOS(カーネル)の名称を調べるメソッドのようで、Linuxやunix階層でのメソッドのようです。だいぶ深い層にある情報を引っ張ってきているってことです。

以下の画像はWikipediaを参照したものですが、カーネルはアプリケーションとデバイスの間に位置するもののようです。

https://upload.wikimedia.org/wikipedia/commons/thumb/8/8f/Kernel_Layout.svg/220px-Kernel_Layout.svg.png

シミュレータ で判定する際はx86_64i386が帰ってくるので検証することができないので注意です。本当に機能するかどうかは実端末で確認する必要があります。

OSSのライブラリも最近できたようでYMTGetDeviceNameというものを見つけました、処理的には同じようなことをしてそうです。

しかしこのライブラリも各端末に対応するデバイスは列挙しないといけないようなのでめちゃくちゃ楽になるというわけではなさそうです。

ピクセルサイズ判別する

iPhone/iPad/Apple Watch解像度(画面サイズ)早見表に端末に対応するピクセル一覧が乗っているのでこれに対応するかを判定しサイズごとに分けることも可能です。

端末を完全に特定することはできませんが、サイズごとに処理を分岐するならこの方法でも良さそうです。

switch (UIScreen.main.nativeBounds.height) {
case 480:
    // iPhone
    // iPhone 3G
    // iPhone 3GS
    break
case 960:
    // iPhone 4
    // iPhone 4S
    break
case 1136:
  ...省略
}

【Swift4】iPhone・iPadなどの機種判定し処理を振り分ける方法【iOS9】というサイトからコードを引用したので詳しく知りたい方は参照してみてください。

参考文献

【Swift2】iOSの端末モデル名を取得する

【Swift】使っている端末の情報を取得

iPhone iPad 端末での 一般名と内部名の対応表

UNAME

システム情報を取得する

iPhone/iPad/Apple Watch解像度(画面サイズ)早見表