MacOSのシングルウィンドウアプリケーションを作るときにやっておくこと

1 min read読了の目安(約1600字

ウィンドウを閉じたら、アプリも閉じるようにする

そうしないと審査で落ちます。

アプリ左上の赤丸の閉じるボタンCommand+Wなんかでユーザーがウィンドウを閉じてしまったときにウィンドウはないけどアプリ自体は存在し続けるのが気に入らない模様です。

たしかに、何も手を打たないと、ウィンドウを閉じてもアプリケーションのメニューバーは残っています。

対応の選択肢としては、ウィンドウを復帰させる機能 を実装するか、 ウィンドウを閉じたら、アプリも閉じるようにする かの二択のようです。

私のケースではウィンドウを復帰させる機能を入れると追加でいろんなことを考えないといけなそうだと思い、ウィンドウを閉じたらアプリも閉じるようにする方針を採用しました。

対応方法

applicationShouldTerminateAfterLastWindowClosed を使うことで対応可能です。

AppDelegate のクラスに以下のように書いてあげれば、ウィンドウを閉じたときにアプリケーションも一緒に終了してくれます。

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
	// シングルアプリケーションとしたいため、ウィンドウを閉じたらアプリケーションも閉じる仕様とする
	return true
}

SwiftUIでAppDelegateとAppを共存させるには?

SwiftUIのエントリポイント(@mainを指定するコード)はAppDelegateAppのどちらかをプロジェクト作成時の最初に選択することになります。
(あとからでもコードをいじれば変えることはできます)

で、上記の対応方法はAppDelegateのクラスに書くのですがプロジェクトでAppを選択している場合は、コードを書けないのかと思ってしまいます。

しかし、以下のようにAppを継承したクラスの中で@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegateを書いておくとAppDelegateのコードに書いた内容が有効になります。

import SwiftUI

@main
struct NiceApp: App {
    var body: some Scene {
	    // プロジェクトのコード
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) {
	    // プロジェクトのコード
    }

    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        // シングルアプリケーションとしたいため、ウィンドウを閉じたらアプリケーションも閉じる仕様とする
        return true
   }
}

@NSApplicationDelegateAdaptorを書くやり方は今回の話に限らず、AppDelegeteでないとできない方法が見つかったときの対応方法として覚えておくと結構いい感じだなと思いました。

そのうち、Appだけでなんでもできるようになっていくのかもしれませんが、それはまだ先になりそうです。

この件に関連して審査で何度かリジェクトをもらったので、知見を共有しました。