Chomado's Blog
You Are Reading

【Xamarin .iOS】ユーザの環境を得る【OS,端末モデル名,アプリのバージョン】

0
C#

【Xamarin .iOS】ユーザの環境を得る【OS,端末モデル名,アプリのバージョン】


ユーザからのバグ報告に, その再現のための情報(その報告してくれたユーザの環境(OSや端末, アプロのバージョン))が欲しいですよね.
Xamarin.iOS において それを引っこ抜く方法を書きました.
Xamarin.Android の場合は一つ前の記事を参照してください.

アプリの動いている環境の情報を引っこ抜く

知りたい項目 格納する変数名
OS ios_ver “iOS 8.4”
デバイス device “iPhone 5S”
このアプリのバージョン app_ver “1.0.0”
このアプリのビルド番号 app_build “150729.0”
//// OS
// iOS 7.0
// iOS 8.4
var ios_ver = UIDevice.CurrentDevice.SystemVersion;


//// iPhone or iPad or iPod
var device_rough = UIDevice.CurrentDevice.Model;

//// デバイスのモデル名
// iPhone5S
// iPad Mini (WiFi)
var device = DeviceModel.GetModelName(); // (これはシステム提供ではなくユーザ定義の関数です! 下に実装を書きます)


//// このアプリのバージョン
// info.plist の Version で定義した値.
// "1.0.0" など
var app_ver = NSBundle.MainBundle.InfoDictionary["CFBundleShortVersionString"];

//// このアプリのビルド番号
// info.plist の Build で定義した値.
// "150729.0" など
var app_build = NSBundle.MainBundle.InfoDictionary["CFBundleVersion"];

デバイスのモデル名について

ここで注意が必要なのは, デバイスのモデル名( iPhone 5S など)です.

Android では簡単に抜けるのですが, iOSの場合はとても面倒くさいです.

UIDevice.CurrentDevice.Model

というそれっぽいものがあるのですが, これでは「iPhone」までしか抜けないのです. iPhone 6 なのか iPhone 4S なのか 全然分からないです. だいぶラフですね

なので, システムが提供してくれていないので, 私たちがそれっぽいのを自前で用意しなければなりません.

以下実装を書きます.

酢酸さん(いつもお世話になっております)の記事( Xamarin.iOSでiOSデバイスの機種名(モデル名)を取得する | 酢ろぐ! )の通りにやると,
例えば iPhone 5S のデバイスのモデル名が “iPhone6,1” という文字列で取れる, というところまでできるので,
ここにその記事中で紹介してあった対応表(“iPhone6,1″→”iPhone 5S”という変換)( UIDeviceHardware ) を適用した版を書きます.

これで DeviceModel.GetModelName() で そのコードが実行された環境の端末名が得られます.

// ユーザの端末のモデル名( "iPhone 6 Plus" など)を取得する
public class DeviceModel
{
    public const string HardwareProperty = "hw.machine";

    [DllImport ("libc", CallingConvention = CallingConvention.Cdecl)]
    static internal extern int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string property,
        IntPtr output, IntPtr oldLen, IntPtr newp, uint newlen);

    public static string GetModelName()
    {
        var deviceVersion = string.Empty;

        var pLength = IntPtr.Zero;
        var pString = IntPtr.Zero;
        try
        {
            pLength = Marshal.AllocHGlobal(sizeof(int));
            sysctlbyname(DeviceModel.HardwareProperty, IntPtr.Zero, pLength, IntPtr.Zero, 0);
            var length = Marshal.ReadInt32(pLength);
            if (length <= 0)
            {
                return string.Empty;
            }

            pString = Marshal.AllocHGlobal(length);
            sysctlbyname(DeviceModel.HardwareProperty, pString, pLength, IntPtr.Zero, 0);

            deviceVersion = Marshal.PtrToStringAnsi(pString);
        }
        finally
        {
            if (pLength != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pLength);
            }
            if (pString != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pString);
            }
        }
        // モデルコードを モデル名に変換してから返す
        return ModelName(model_code: deviceVersion);
    }

    // モデルコードを モデル名に変換する人. 
    // 新しいモデルが出たらここに追加していかなければならないので, この表はどこかオンライン上のライブラリから取ってくる感じにしたほうが良い. でもどうやってやればいいのか分からないから, 私はここにハードコーディングしてしまいます  
    private static string ModelName(string model_code)
    {
        switch (model_code)
        {
            case "iPhone1,1":   return "iPhone 1G";
            case "iPhone1,2":   return "iPhone 3G";
            case "iPhone2,1":   return "iPhone 3GS";
            case "iPhone3,1":   return "iPhone 4 (GSM)";
            case "iPhone3,3":   return "iPhone 4 (CDMA)";
            case "iPhone4,1":   return "iPhone 4S";
            case "iPhone5,1":   return "iPhone 5 (GSM)";
            case "iPhone5,2":   return "iPhone 5 (CDMA)";
            case "iPhone5,3":   return "iPhone 5C";
            case "iPhone5,4":   return "iPhone 5C";
            case "iPhone6,1":   return "iPhone 5S";
            case "iPhone6,2":   return "iPhone 5S";
            case "iPhone7,1":   return "iPhone 6 Plus";
            case "iPhone7,2":   return "iPhone 6";

            case "iPod1,1": return "iPod Touch 1G";
            case "iPod2,1": return "iPod Touch 2G";
            case "iPod3,1": return "iPod Touch 3G";
            case "iPod4,1": return "iPod Touch 4G";
            case "iPod5,1": return "iPod Touch 5G";
            case "iPod7,1": return "iPod Touch 6G";

            case "iPad1,1": return "iPad";
            case "iPad2,1": return "iPad 2 (WiFi)";
            case "iPad2,2": return "iPad 2 (GSM)";
            case "iPad2,3": return "iPad 2 (CDMA)";
            case "iPad2,4": return "iPad 2 (WiFi)";
            case "iPad2,5": return "iPad Mini (WiFi)";
            case "iPad2,6": return "iPad Mini (GSM)";
            case "iPad2,7": return "iPad Mini (CDMA)";
            case "iPad3,1": return "iPad 3 (WiFi)";
            case "iPad3,2": return "iPad 3 (CDMA)";
            case "iPad3,3": return "iPad 3 (GSM)";
            case "iPad3,4": return "iPad 4 (WiFi)";
            case "iPad3,5": return "iPad 4 (GSM)";
            case "iPad3,6": return "iPad 4 (CDMA)";

            case "iPad4,1": return "iPad Air (WiFi)";
            case "iPad4,2": return "iPad Air (GSM)";
            case "iPad4,3": return "iPad Air (CDMA)";
            case "iPad5,3": return "iPad Air 2 (WiFi)";
            case "iPad5,4": return "iPad Air 2 (CDMA)";

            case "iPad4,4": return "iPad Mini Retina (WiFi)";
            case "iPad4,5": return "iPad Mini Retina (CDMA)";
            case "iPad4,7": return "iPad Mini 3 (WiFi)";
            case "iPad4,8": return "iPad Mini 3 (CDMA)";
            case "iPad4,9": return "iPad Mini 3 (CDMA)";
        }
        // 未定義の場合, 仕方ないのでそのまま返す
        return model_code;
    }
}

↑ 47行目のコメントでも書きましたが, これだと, 新しいモデル(iPhone 7 とか将来)出たら, そのたびにここに対応表を追加していかなければならない運用になるので, できればこれはオンライン上のどこかのライブラリから引っ張ってくる形にするべきです. でも私はそれをどうやってやればいいのか分からないので取り敢えず今はハードコーディングしてしまいます. まずは動くことが大切!!!!!!!!!!!!!! (誰か良い方法があったら教えて下さい…)


Madoka Chomado (ちょまど)

千代田まどかです。よく「ちょまど」と呼ばれます。Microsoft 社員。文系出身プログラマ兼マンガ家です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です