UIDesignRequiresCompatibility を変更して、Liquid Glass をランタイムで切り替える
はじめに
どうも tattsun です 🙌
「クラシルリワード」の名称が 「レシチャレ」 に変わりました 🎉
これからより一層、ユーザー価値を届けていけるように開発に取り組んでいこうと思います!
Liquid Glass を無効にする方法
無効にするのは、とても簡単で Info.plist の UIDesignRequiresCompatibility を YES にするだけです。
<key>UIDesignRequiresCompatibility</key>
<true/>
ただし、この設定が利用可能なのは次のメジャーリリースまでのようです。

UIDesignRequiresCompatibility を動的に切り替える
上記の通り、いつかは対応しなければいけないということでこの残された時間で計画的に対応が必要になります。そこで、開発や確認がしやすいように動的に Liquid Glass を切り替えられるようにしようというのが今回の目的です。
基本的には、こちらの記事を参考にさせていただきました 🙇
swizzling で Info.plist の値を書き換える
UserDefaults に保持している値を参照して、UIDesignRequiresCompatibility を書き換えます。
extension Bundle {
static func swizzleInfoDictionary() {
guard #available(iOS 26.0, *) else { return }
let originalSelector = #selector(getter: infoDictionary)
let swizzledSelector = #selector(getter: swizzledInfoDictionary)
guard let original = class_getInstanceMethod(Self.self, originalSelector),
let swizzled = class_getInstanceMethod(Self.self, swizzledSelector)
else { return }
method_exchangeImplementations(original, swizzled)
}
@objc private var swizzledInfoDictionary: NSDictionary? {
let dict = NSMutableDictionary(
dictionary: self.swizzledInfoDictionary ?? [:]
)
dict["UIDesignRequiresCompatibility"] = UserDefaults.standard.bool(forKey: "isLiquidGlassDisabled")
return dict
}
}
アプリ起動時に反映する
AppDelegate で Info.plist の書き換え処理を呼び出します。
これで表示の切り替えが UserDefaults の値で出来るようになりました 🎉
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
#if DEBUG
// ✅ swizzling を利用して、Liquid Glass の設定を書き換える
// window が初期化される前に実行する必要がある
Bundle.swizzleInfoDictionary()
#endif
// ❌ UIWindow を初期化した後だと読み込まれなかった
self.window = UIWindow()
// setup ...
}
なぜ UIWindow の初期化に左右されるのか
検証用のコードを用意して、挙動を確認してみる。
private func fetchLiquidGlassOption() -> String {
if let value = Bundle.main.object(forInfoDictionaryKey: "UIDesignRequiresCompatibility") as? Bool {
value ? "YES" : "NO"
} else {
"UNKNOWN"
}
}
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let before = fetchLiquidGlassOption()
window = UIWindow()
#if DEBUG
// swizzling を利用して、Liquid Glass の設定を書き換える
// UIWindow が初期化された時点で Liquid Glass の設定値が反映されるため
// UIWindow が初期化される前に実行する必要がある
Bundle.swizzleInfoDictionary()
#endif
let after = fetchLiquidGlassOption()
print("🚀 isLiquidGlassEnabled: \(!UserDefaults.standard.bool(forKey: "isLiquidGlassDisabled"))")
print("🚀 [Before] UIDesignRequiresCompatibility: \(before)")
print("🚀 [After] UIDesignRequiresCompatibility: \(after)")
// setup ...
}
上記のようなコードを書いても Bundle から取得する Info.plist の値は処理の順番に関わらず不変(設定値が維持されている状態)だった。つまり、swizzling で置き換えている infoDictionary は Info.plist とは別物ということがわかる。
-
infoDictionary: 内部で利用するためのプロパティ。Info.plistの値を読み込んでいる。
-
Bundle.main.object(forInfoDictionaryKey:):Info.plistの値を取得する
順番的には、以下のようになっていると推測している。
-
Bundle.infoDictionaryにInfo.plistの設定値が読み込まれる -
UIWindowが初期化されるタイミングでBundle.infoDictionaryのUIDesignRequiresCompatibilityを参照して、Liquid Glass を適用するかを決定
ChatGPT など自分でもこの動作の裏付けの調査を行いましたが、特に情報はありませんでした… 😭
おわりに
設定値を変更した後に再起動が必要ですが、ビルドし直す必要がなくなったため開発が捗りそうです!
次回は、Xcode 26 対応の全体像についても記事を書こうと思っています ✨
Discussion