メニューバーに常駐するアプリを開発するときは、Dockにアイコンを表示したくない場合が多い。Info.plistのLSUIElementがtrueのアプリは、Dockから隠れるエージェントアプリとして扱われる。

Xcodeでは、ターゲットのビルド設定INFOPLIST_KEY_LSUIElementをYESにする。

GENERATE_INFOPLIST_FILE = YES
INFOPLIST_KEY_LSUIElement = YES

INFOPLIST_KEY_LSUIElementが有効になるのは、GENERATE_INFOPLIST_FILE = YES(Info.plistを自動生成する設定)のときである。

実行例はXcode 27.0、macOS 27.0.1のもの。

ビルド設定でLSUIElementを設定する

Xcodeでは、ターゲットの「Build Settings」でUIElementを検索する。「Info.plist Values」の「Application is Agent (UIElement)」が、INFOPLIST_KEY_LSUIElementである。

INFOPLIST_KEY_LSUIElementに指定した値は、ビルド後のInfo.plistに、bool型のLSUIElementとして出力される。

INFOPLIST_KEY_LSUIElementの値出力されるLSUIElement
YES、true、1true
NO、falsefalse
設定しないキーが出力されない

ビルドしたアプリのInfo.plistは、plutilで確認できる。

$ plutil -extract LSUIElement raw [アプリ名].app/Contents/Info.plist
true

ビルド設定の値は、xcodebuild -showBuildSettingsで確認できる。

$ xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] -showBuildSettings 2>/dev/null | grep -E "^\s+(GENERATE_INFOPLIST_FILE|INFOPLIST_KEY_LSUIElement) ="
    GENERATE_INFOPLIST_FILE = YES
    INFOPLIST_KEY_LSUIElement = YES

参考: 【xcodebuild】MACOSX_DEPLOYMENT_TARGETの優先関係を-showBuildSettingsで確認する

Dockに表示されないことを確認する

LSUIElementがtrueのアプリは、UIElementの種類のアプリとして扱われる。Appleのドキュメントでは、バックグラウンドで実行され、Dockには表示されないエージェントアプリと説明されている。

参考: LSUIElement | Apple Developer Documentation

アプリの種類は、lsappinfoで確認できる。open -gでアプリをバックグラウンドで起動し、バンドルIDを指定する。

$ open -g [アプリ名].app
$ lsappinfo info -only applicationType -app [バンドルID] | grep -o 'type="[A-Za-z]*"'
type="UIElement"

LSUIElementを設定していないアプリは、Foregroundになる。Foregroundのアプリは、起動するとDockにアイコンが表示される。

$ lsappinfo info -only applicationType -app [バンドルID] | grep -o 'type="[A-Za-z]*"'
type="Foreground"

手書きのInfo.plistと併用する

SparkleのSUFeedURLのように、INFOPLIST_KEY_のビルド設定が用意されていないキーは、INFOPLIST_KEY_では設定できない。このようなキーは、手書きのInfo.plistをINFOPLIST_FILEに指定して設定する。GENERATE_INFOPLIST_FILE = YESのままであれば、自動生成される内容と、手書きのInfo.plistの内容がマージされる。

参考: 【Sparkle】SwiftPMで導入してアプリに更新チェックを組み込む

手書きのInfo.plistとINFOPLIST_KEY_LSUIElementの両方に、LSUIElementを設定した場合の結果は次のとおりである。

GENERATE_INFOPLIST_FILE手書きのInfo.plistのLSUIElementINFOPLIST_KEY_LSUIElementビルド後のLSUIElement
YESなしYEStrue
YEStrueなしtrue
YESfalseなしfalse
YESfalseYEStrue
YEStrueNOfalse
NOfalseYESfalse

両方に設定すると、INFOPLIST_KEY_LSUIElementの値が優先される。手書きのInfo.plistにだけ設定した場合は、その値が、ビルド後のInfo.plistに残る。

GENERATE_INFOPLIST_FILE = NOのときは、INFOPLIST_KEY_LSUIElementが無視される。手書きのInfo.plistの値が、そのまま使われる。

GENERATE_INFOPLIST_FILE = NOで、INFOPLIST_FILEも指定しないと、ビルドに失敗する。

error: Build input file cannot be found: '[ビルド成果物のパス]/[アプリ名].app/Contents/Info.plist'. Did you forget to declare this file as an output of a script phase or custom build rule which produces it?

どちらの方法を選ぶか

LSUIElementは、INFOPLIST_KEY_LSUIElementが用意されているキーである。ビルド設定だけで済み、手書きのInfo.plistを用意しなくてよい。

Sparkleの設定のように、手書きのInfo.plistが必要なプロジェクトでも、LSUIElementはINFOPLIST_KEY_LSUIElementに設定できる。ただし、同じキーを両方に設定すると、手書きのInfo.plistの値が使われなくなる。どちらか一方に統一すると、設定を確認しやすい。

手書きのInfo.plistを使う場合は、PlistBuddyで値を確認できる。

$ /usr/libexec/PlistBuddy -c "Print :LSUIElement" Info.plist
true

参考: 【PlistBuddy】Info.plistの値をコマンドで読み書きする