Mac OSXスクリーンセーバーをSwiftで作成する

この記事は[Cocoa Advent Calendar 2015](http://qiita.com/advent-calendar/2015/cocoa)の2日目の記事です。

## はじめに

スクリーンセーバーのアプリを作った人なんてほとんどいないと思いますが、その手始めにSwiftでの起動方法まで紹介します。

また、今回のプロジェクトは [github.com/kaneshin/TextSaver](https://github.com/kaneshin/TextSaver) に置いておきます。

## 環境

- OS X El Capitan 10.11.1 (15B42)
- Xcode 7.1.1 (7B1005)

## 準備

### ScreenSaverEngine.appの用意

実装しているScreen Saverを実行するためにScreenSaver.frameworkにあるScreenSaverEngine.appをApplicationsディレクトリへシンボリックリンクを作成します。

```sh
$ ln -sf /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app /Applications/ScreenSaverEngine.app
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/3353/d6c50fb3-af46-214d-7bfa-cfcee0f7f410.png" height=300 />

## 実装

### Screen Saverテンプレート

<img src="https://qiita-image-store.s3.amazonaws.com/0/3353/600b2caa-9354-ec25-75b4-dd00b4f146fe.png" height=300 />

テンプレートを作成するとObjective-Cのテンプレートしかないため、Swiftファイルを用意する。この時、**なるべく同じファイル名にすること。**

![Screen Shot 2015-11-29 at 4.03.00 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/b9b018be-3a33-2f47-8c6a-56469bec5cc5.png)

作成したら、Objective-C用のテンプレファイルは削除してください。

### Swift用のコードテンプレート

作成したSwiftファイルにそのまま下記をペーストしてください。

```swift
import Foundation
import ScreenSaver

class TextSaverView: ScreenSaverView {

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        self.animationTimeInterval = 1.0 / 30.0
    }

    override init?(frame: NSRect, isPreview: Bool) {
        super.init(frame: frame, isPreview: isPreview)
        self.animationTimeInterval = 1.0 / 30.0
    }

    override func startAnimation() {
        super.startAnimation()
    }

    override func stopAnimation() {
        super.stopAnimation()
    }

    override func drawRect(rect: NSRect) {
        super.drawRect(rect)
    }

    override func animateOneFrame() {
        return
    }

    override func hasConfigureSheet() -> Bool {
        return false
    }

    override func configureSheet() -> NSWindow? {
        return nil
    }
    
}
```

### Target Settings

#### `Principal class` の設定

テンプレ作成時のObjective-Cとは違う命名でSwiftファイルを作成した場合、`Principal class` の値を変更する必要があります。

![Screen Shot 2015-11-29 at 4.09.54 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/5477bac8-2b0b-175e-2103-1ba8958e7816.png)

### `Embedded Content Contains Swift Code` の設定

Swiftファイルを実行するため、下記の通りに値を `Yes` に変更してください

![Screen Shot 2015-11-29 at 4.10.37 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/d0409bbd-e50c-cecb-12a6-ab8501ed71a0.png)

## 起動

Runで実装しているスクリーンセーバーをScreenSaverEngine.appでバックグラウンド起動できるようにします。

### Schemeの設定

InfoにあるExecuteへScreenSaverEngine.appを設定する

![Screen Shot 2015-11-29 at 4.14.07 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/c0b6f244-4546-f165-30e3-9276f2768aec.png)

### Argumentsの設定

ArgumentsにあるArguments Passed on Launchに下記の値を設定する

- `-background`
- `-module "$(EXECUTABLE_NAME)"`

![Screen Shot 2015-11-29 at 4.15.09 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/babefcc7-dfc3-a982-3d80-9018a027d3e6.png)

### Run Scriptの設定

実装しているスクリーンセーバーをスクリーンセーバー用のディレクトリにコピーするために下記のコマンドを設定する

- `open -W "${CODESIGNING_FOLDER_PATH}"`

_New Run Script Phase_
![Screen Shot 2015-11-29 at 4.16.13 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/482bee16-aaa2-8db2-9779-f8a59f40ac56.png)

_Run Script_
![Screen Shot 2015-11-29 at 4.16.33 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/bdf4a0c5-f44b-68fe-435f-146f3081306e.png)


### Run - 実行

Command+Rで実行すると、下記の画像のようにScreenSaverのインストールを求められるのでそのまま `Install` してください

![Screen Shot 2015-11-29 at 4.18.50 PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/e6fdfa6b-8007-25e5-0674-5473d69b8021.png)

インストールするとDesktop & Screen Saverの画面が開きますが、そのまま閉じてください。

![Screen_Shot_2015-11-29_at_4_19_01_PM.png](https://qiita-image-store.s3.amazonaws.com/0/3353/fce6268e-1406-f9c1-4fc3-80a65384e0e8.png)

すると、デスクトップでスクリーンセーバーが動いていることが確認できると思います。

## 起動しない場合

起動時のデバッグメッセージに

```
Message from debugger: cannot attach to process due to System Integrity Protection
```

というメッセージが出る場合はプロテクションが有効になっているため、それを無効にします。

[How to Disable System Integrity Protection (rootless) in OS X El Capitan](http://osxdaily.com/2015/10/05/disable-rootless-system-integrity-protection-mac-os-x/)

- Recoveryモードで起動し、ターミナルを開く
- `csrutil disable; reboot` をタイプ

## おわりに

iOS開発しかしたことが無い方が多数だと思うのですが、OSX開発はiOS開発のようにフレームワークが**手厚くない**のと、ググっても記事が**出てこない**ですが結構楽しいです。

スクリーンセーバー作成の初歩中の初歩を紹介したので、仮に、好評そうであれば追加の実装方法を別記事で紹介したいと思います。

今回はひとまずここまでです。