はじめに
iPhone6,1
のようなデバイスモデル名を取得したいことがありますが、ググってもリフレクションを使った記事しか見つかりません。
Twitterでリフレクションを使わずに取得する方法を教えていただいた ので紹介します。
環境
- OS:macOS Ventura 13.4.1
- Swift:5.8
デバイスモデル名を取得する方法
デバイスモデル名を取得する方法です。
リフレクションあり
まずはリフレクションを使う方法です。
以下の記事を参考に実装します。
import UIKit
extension UIDevice {
static var modelName: String {
var systemInfo = utsname()
uname(&systemInfo)
let mirror = Mirror(reflecting: systemInfo.machine)
let identifier = mirror.children.reduce("") { identifier, element in
guard
let value = element.value as? Int8,
value != 0
else {
return identifier
}
return identifier + String(UnicodeScalar(UInt8(value)))
}
return identifier
}
}
実際に呼び出せることを確認します。
print("Device model: \(UIDevice.modelName)")
無事にデバイスモデル名が出力されました。
Device model: arm64
Swift Playgroundsで実行したため arm64
と出力されています。
リフレクションなし
次にリフレクションを使わずに実装します。
import UIKit
extension UIDevice {
static var modelName: String {
var systemInfo = utsname()
uname(&systemInfo)
let chars: [CChar] = [
systemInfo.machine.0,
systemInfo.machine.1,
systemInfo.machine.2,
systemInfo.machine.3,
systemInfo.machine.4,
systemInfo.machine.5,
systemInfo.machine.6,
systemInfo.machine.7,
systemInfo.machine.8,
systemInfo.machine.9,
systemInfo.machine.10,
systemInfo.machine.11,
systemInfo.machine.12,
systemInfo.machine.13,
systemInfo.machine.14,
systemInfo.machine.15,
systemInfo.machine.16,
systemInfo.machine.17,
systemInfo.machine.18,
systemInfo.machine.19,
systemInfo.machine.20,
systemInfo.machine.21,
systemInfo.machine.22,
systemInfo.machine.23,
systemInfo.machine.24,
systemInfo.machine.25,
systemInfo.machine.26,
systemInfo.machine.27,
systemInfo.machine.28,
systemInfo.machine.29,
systemInfo.machine.30,
systemInfo.machine.31,
systemInfo.machine.32,
systemInfo.machine.33,
systemInfo.machine.34,
systemInfo.machine.35,
systemInfo.machine.36,
systemInfo.machine.37,
systemInfo.machine.38,
systemInfo.machine.39,
systemInfo.machine.40,
systemInfo.machine.41,
systemInfo.machine.42,
systemInfo.machine.43,
systemInfo.machine.44,
systemInfo.machine.45,
systemInfo.machine.46,
systemInfo.machine.47,
systemInfo.machine.48,
systemInfo.machine.49,
systemInfo.machine.50,
systemInfo.machine.51,
systemInfo.machine.52,
systemInfo.machine.53,
systemInfo.machine.54,
systemInfo.machine.55,
systemInfo.machine.56,
systemInfo.machine.57,
systemInfo.machine.58,
systemInfo.machine.59,
systemInfo.machine.60,
systemInfo.machine.61,
systemInfo.machine.62,
systemInfo.machine.63,
systemInfo.machine.64,
systemInfo.machine.65,
systemInfo.machine.66,
systemInfo.machine.67,
systemInfo.machine.68,
systemInfo.machine.69,
systemInfo.machine.70,
systemInfo.machine.71,
systemInfo.machine.72,
systemInfo.machine.73,
systemInfo.machine.74,
systemInfo.machine.75,
systemInfo.machine.76,
systemInfo.machine.77,
systemInfo.machine.78,
systemInfo.machine.79,
systemInfo.machine.80,
systemInfo.machine.81,
systemInfo.machine.82,
systemInfo.machine.83,
systemInfo.machine.84,
systemInfo.machine.85,
systemInfo.machine.86,
systemInfo.machine.87,
systemInfo.machine.88,
systemInfo.machine.89,
systemInfo.machine.90,
systemInfo.machine.91,
systemInfo.machine.92,
systemInfo.machine.93,
systemInfo.machine.94,
systemInfo.machine.95,
systemInfo.machine.96,
systemInfo.machine.97,
systemInfo.machine.98,
systemInfo.machine.99,
systemInfo.machine.100,
systemInfo.machine.101,
systemInfo.machine.102,
systemInfo.machine.103,
systemInfo.machine.104,
systemInfo.machine.105,
systemInfo.machine.106,
systemInfo.machine.107,
systemInfo.machine.108,
systemInfo.machine.109,
systemInfo.machine.110,
systemInfo.machine.111,
systemInfo.machine.112,
systemInfo.machine.113,
systemInfo.machine.114,
systemInfo.machine.115,
systemInfo.machine.116,
systemInfo.machine.117,
systemInfo.machine.118,
systemInfo.machine.119,
systemInfo.machine.120,
systemInfo.machine.121,
systemInfo.machine.122,
systemInfo.machine.123,
systemInfo.machine.124,
systemInfo.machine.125,
systemInfo.machine.126,
systemInfo.machine.127,
systemInfo.machine.128,
systemInfo.machine.129,
systemInfo.machine.130,
systemInfo.machine.131,
systemInfo.machine.132,
systemInfo.machine.133,
systemInfo.machine.134,
systemInfo.machine.135,
systemInfo.machine.136,
systemInfo.machine.137,
systemInfo.machine.138,
systemInfo.machine.139,
systemInfo.machine.140,
systemInfo.machine.141,
systemInfo.machine.142,
systemInfo.machine.143,
systemInfo.machine.144,
systemInfo.machine.145,
systemInfo.machine.146,
systemInfo.machine.147,
systemInfo.machine.148,
systemInfo.machine.149,
systemInfo.machine.150,
systemInfo.machine.151,
systemInfo.machine.152,
systemInfo.machine.153,
systemInfo.machine.154,
systemInfo.machine.155,
systemInfo.machine.156,
systemInfo.machine.157,
systemInfo.machine.158,
systemInfo.machine.159,
systemInfo.machine.160,
systemInfo.machine.161,
systemInfo.machine.162,
systemInfo.machine.163,
systemInfo.machine.164,
systemInfo.machine.165,
systemInfo.machine.166,
systemInfo.machine.167,
systemInfo.machine.168,
systemInfo.machine.169,
systemInfo.machine.170,
systemInfo.machine.171,
systemInfo.machine.172,
systemInfo.machine.173,
systemInfo.machine.174,
systemInfo.machine.175,
systemInfo.machine.176,
systemInfo.machine.177,
systemInfo.machine.178,
systemInfo.machine.179,
systemInfo.machine.180,
systemInfo.machine.181,
systemInfo.machine.182,
systemInfo.machine.183,
systemInfo.machine.184,
systemInfo.machine.185,
systemInfo.machine.186,
systemInfo.machine.187,
systemInfo.machine.188,
systemInfo.machine.189,
systemInfo.machine.190,
systemInfo.machine.191,
systemInfo.machine.192,
systemInfo.machine.193,
systemInfo.machine.194,
systemInfo.machine.195,
systemInfo.machine.196,
systemInfo.machine.197,
systemInfo.machine.198,
systemInfo.machine.199,
systemInfo.machine.200,
systemInfo.machine.201,
systemInfo.machine.202,
systemInfo.machine.203,
systemInfo.machine.204,
systemInfo.machine.205,
systemInfo.machine.206,
systemInfo.machine.207,
systemInfo.machine.208,
systemInfo.machine.209,
systemInfo.machine.210,
systemInfo.machine.211,
systemInfo.machine.212,
systemInfo.machine.213,
systemInfo.machine.214,
systemInfo.machine.215,
systemInfo.machine.216,
systemInfo.machine.217,
systemInfo.machine.218,
systemInfo.machine.219,
systemInfo.machine.220,
systemInfo.machine.221,
systemInfo.machine.222,
systemInfo.machine.223,
systemInfo.machine.224,
systemInfo.machine.225,
systemInfo.machine.226,
systemInfo.machine.227,
systemInfo.machine.228,
systemInfo.machine.229,
systemInfo.machine.230,
systemInfo.machine.231,
systemInfo.machine.232,
systemInfo.machine.233,
systemInfo.machine.234,
systemInfo.machine.235,
systemInfo.machine.236,
systemInfo.machine.237,
systemInfo.machine.238,
systemInfo.machine.239,
systemInfo.machine.240,
systemInfo.machine.241,
systemInfo.machine.242,
systemInfo.machine.243,
systemInfo.machine.244,
systemInfo.machine.245,
systemInfo.machine.246,
systemInfo.machine.247,
systemInfo.machine.248,
systemInfo.machine.249,
systemInfo.machine.250,
systemInfo.machine.251,
systemInfo.machine.252,
systemInfo.machine.253,
systemInfo.machine.254,
systemInfo.machine.255,
systemInfo.machine.254,
systemInfo.machine.255,
]
return String(cString: chars)
}
}
愚直ですが、実際に呼び出せます。
print("Device model: \(UIDevice.modelName)") // Device model: arm64
解説
まず systemInfo.machine
は256つの要素を持ったタプルです。
public struct utsname {
// ...
public var machine: (CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar, CChar) /* [XSI] Hardware type */
}
タプルは .x
でx番目の要素へアクセスできるため、 systemInfo.machine.x
と書いていました。
そして String
への変換はタプルでなく配列で CChar
を用意する必要があるため、愚直にタプルの要素を1つずつ配列へ格納していた、ということです。
余談
systemInfo.machine.x
は1文字ずつに対応しているので、 arm64
なら chars
の中身が systemInfo.machine.5
まででも問題なく実行できます。
let chars: [CChar] = [
systemInfo.machine.0,
systemInfo.machine.1,
systemInfo.machine.2,
systemInfo.machine.3,
systemInfo.machine.4,
systemInfo.machine.5,
]
では配列の中身が以下ならどう出力されるでしょうか。
let chars: [CChar] = [
systemInfo.machine.1,
systemInfo.machine.2,
systemInfo.machine.4,
systemInfo.machine.5,
]
答えは以下です。
print("Device model: \(UIDevice.modelName)") // Device model: rm4
1(0)番目の a
と4(3)番目の 6
が抜けてしまいました。
表にするとわかりやすいです。
変数 | 値 | 文字 |
---|---|---|
systemInfo.machine.0 |
97 |
a |
systemInfo.machine.1 |
114 |
r |
systemInfo.machine.2 |
109 |
m |
systemInfo.machine.3 |
54 |
6 |
systemInfo.machine.4 |
52 |
4 |
systemInfo.machine.5 〜 systemInfo.machine.255
|
0 |
NULL |
あとは終端がNULL( 0
)でないと実行時エラーが発生します。
let chars: [CChar] = [
systemInfo.machine.1,
systemInfo.machine.2,
systemInfo.machine.4,
]
// ❌: Input of String.init(cString:) must be null-terminated
return String(cString: chars)
最初の例は systemInfo.machine.5
が 0
なので成功しました。
終端は systemInfo.machine.6
でも 0
でも .init()
でも問題ありません。
let chars: [CChar] = [
systemInfo.machine.1,
systemInfo.machine.2,
systemInfo.machine.4,
systemInfo.machine.5, // `systemInfo.machine.6` や `0` 、 `.init()` などでもOK
]
おわりに
リフレクションを多用するのはよくないですが、タプルを配列へ変換するのに便利ということがわかりました。
リフレクションは用法・用量を守って使っていきたいと思います。