iOS16からデベロッパモードが導入
iOS16からiPhoneで開発する際には「デベロッパモード」を有効にする必要があります。
https://qiita.com/YokohamaHori/items/d00e1786c34b4ab30638
iOS16βではデフォルトで「設定」にデベロッパモードの項目が表示されていますが、製品版では表示されなくなったようです。
原則としてMac(XCode入り)につないで項目を表示させてから、ONにする必要があります。
(↑ この部分で何らかの命令を飛ばしている?)
Windowsのみでデベロッパモードを有効にする
通常はiPhoneの開発をするのにMacがないなんて考えられないのですが、なんやかんやあって、WindowsのみでデベロッパモードをONにする必要が生じました。
iOSデバイス用のオープンソースライブラリであるlibimobiledeviceでは、対応してそうなissueがありますが、windows用のビルドが通らない & nuget版も長らく更新していない。
- 方針 とりあえず既存のlibimobiledeviceのDLLを叩いて、無理やりデベロッパモードを出現させる。
コーディング
前提
- VisualStudioとC#を使用します。
- nugetでlibimobiledeviceをインストールします。
- 適当なクラスを作成してそのメソッドとして構築しています。(以下ではIosDeviceクラスとして作成)
//iPhoneを識別するためのudid
private readonly string udid;
//libimobiledeviceのAPIを叩くための宣言
private IScreenshotrApi sc;
private ScreenshotrClientHandle clientHandle;
//コンストラクタ
public IosDevice()
{
NativeLibraries.Load();
//まずUDIDの取得
int count = 0;
IiDeviceApi idevice = LibiMobileDevice.Instance.iDevice;
iDeviceError ret = idevice.idevice_get_device_list(out ReadOnlyCollection<string> udids, ref count);
if (ret == iDeviceError.NoDevice)
{
throw new Exception("Connot recognize iOS devices.");
}
ret.ThrowOnError();
if (udids.Count == 0)
{
throw new Exception("Connot recognize iOS devices.");
}
//2台以上接続は不可
if (udids.Count > 1)
{
throw new Exception("Do not connect multiple devices.");
}
this.udid = udids[0];
}
iOSバージョンの取得
//libimobiledeviceのexeファイルのCコードを、C#に改造しています
NativeLibraries.Load();
IiDeviceApi idevice = LibiMobileDevice.Instance.iDevice;
iDeviceHandle deviceHandle;
//今後、ほとんどのメソッドは、引数にoutを指定して結果を保存している(戻り値は成否)
try
{
//デバイスハンドルの取得
idevice.idevice_new(out deviceHandle, this.udid).ThrowOnError();
}
catch (Exception ex)
{
throw ex;
}
ILockdownApi lockdown = LibiMobileDevice.Instance.Lockdown;
LockdownClientHandle lockdownHandle;
try
{
//ロックダウンハンドルの取得
lockdown.lockdownd_client_new_with_handshake(deviceHandle, out lockdownHandle, "AP");
}
catch
{
throw new Exception("Connot recognize iOS devices.");
}
//iOSのバージョンを取得
string version;
try
{
lockdown.lockdownd_get_value(lockdownHandle, null, "ProductVersion", out tested);
tested.Api.Plist.plist_get_string_val(tested, out version);
}
catch(Exception ex)
{
throw new Exception(ex.Message);
}
int majorVersion;
//iOSのバージョン表記は〇.〇.〇
Int32.TryParse(version.Split('.')[0], out majorVersion);
iOSバージョンが16以上の場合デベロッパモードを表示
//iOS16以上の場合は開発モード必須
if (majorVersion >= 16){
//デベロッパモードの状態取得
if (lockdown.lockdownd_get_value(lockdownHandle,
"com.apple.security.mac.amfi", "DeveloperModeStatus", out PlistHandle devMode) != LockdownError.Success)
{
throw new Exception("Failed to acquire developer mode.");
}
//デベロッパモードが無効の場合、有効化
char dev_mode_status = '\0';
devMode.Api.Plist.plist_get_bool_val(devMode, ref dev_mode_status);
if (dev_mode_status == '\0')
{
if (lockdown.lockdownd_start_service(lockdownHandle, "com.apple.amfi.lockdown",
out LockdownServiceDescriptorHandle amfiService) != LockdownError.Success)
{
throw new Exception("Failed to acquire developer mode service.");
}
IPropertyListServiceApi pList = LibiMobileDevice.Instance.PropertyListService;
if (pList.property_list_service_client_new(deviceHandle, amfiService,
out PropertyListServiceClientHandle amfi) != PropertyListServiceError.Success)
{
throw new Exception("Failed to acquire developer status.");
}
PlistHandle dict = devMode.Api.Plist.plist_new_dict();
// devMode.Api.Plist.plist_new_uintの値
// 1 = 自動ON(パスコードなし時のみ可能)0 = 項目表示のみ
devMode.Api.Plist.plist_dict_set_item(dict, "action", devMode.Api.Plist.plist_new_uint(0));
if (pList.property_list_service_send_xml_plist(amfi, dict) != PropertyListServiceError.Success)
{
throw new Exception("Request to display DeveloperMode failed.");
}
if (pList.property_list_service_receive_plist(amfi, out dict) != PropertyListServiceError.Success)
{
throw new Exception("Request to display DeveloperMode denied.");
}
PlistHandle errorValue = devMode.Api.Plist.plist_dict_get_item(dict, "Error");
if (!errorValue.IsInvalid)
{
devMode.Api.Plist.plist_get_string_val(errorValue, out string err);
throw new Exception("An error occurred when changing DeveloperMode.\n" + err);
}
PlistHandle val = devMode.Api.Plist.plist_dict_get_item(dict, "success");
char success = '0';
if (!val.IsInvalid)
{
devMode.Api.Plist.plist_get_bool_val(val, ref success);
}
if (success == '0')
{
throw new Exception("Failed to display DeveloperMode.");
}
}
}