iTunesで再生中の曲を自前のサーバに送信するMacアプリ


#なぜ作ったか

社内にBluetoothで接続できるスピーカーが設置されたので、曲をかけている人が何を再生しているのか共有できたら面白いと思って作った。
最終的にHubotにPOSTしてSlackの特定チャンネルに流すことを想定している。

#おことわり
普段はJavascriptを書いている人間なので、Macアプリの綺麗な実装法などは理解していない。もちろんSwiftを触ったのもはじめてである。

#アプリ実装

##TrackInfoController
このクラスはiTunesの通知を受け取る。そのアーティスト名と曲名をViewに送り、またサーバに送信することを担当する。

```swift:TrackInfoController.swift
import Foundation

protocol TrackInfoDelegate: class {
    func updateTrackInfo(info: Dictionary<String,String>)
}

class TrackInfoController: NSObject {
    var delegate: TrackInfoDelegate! = nil
    
    override init() {
    }
    
    func onPlay (notification: NSNotification?) {
        let artist = notification?.userInfo?["Artist"] as? String ?? "none"
        let name = notification?.userInfo?["Name"] as? String ?? "none"
        let info = ["artist": artist, "name": name]
        delegate.updateTrackInfo(info)
        
        let str = "artist=" + artist + "&name=" + name
        let strData = str.dataUsingEncoding(NSUTF8StringEncoding)
        var url = NSURL(string: "http://localhost:3000/") //サーバは別に用意する
        var request = NSMutableURLRequest(URL: url!)
        
        request.HTTPMethod = "POST"
        request.HTTPBody = strData
        
        NSURLConnection.sendAsynchronousRequest(request, queue: nil, completionHandler: self.onComplete)
    }
    
    func onComplete (response: NSURLResponse!, data: NSData!, error: NSError!) {
        println(response)
        println(data)
    }
}
```

Delegateも一緒に定義しておく。
通知がきたらアーティスト名と曲名の辞書を作り、Delegateに渡してあとの処理はViewに任せる。

##ViewController

```swift:ViewController.swift
import Cocoa

class ViewController: NSViewController, TrackInfoDelegate {

    var trackInfoController = TrackInfoController()
    @IBOutlet weak var artistLabel: NSTextField!
    @IBOutlet weak var nameLabel: NSTextField!
    override func viewDidLoad() {
        super.viewDidLoad()
        trackInfoController.delegate = self
        NSDistributedNotificationCenter.defaultCenter().addObserver(trackInfoController, selector: "onPlay:", name:"com.apple.iTunes.playerInfo", object: nil)
        // iTunesで曲が変わった通知は "com.apple.iTunes.playerInfo"
    }

    override var representedObject: AnyObject? {
        didSet {
        }
    }
    
    func updateTrackInfo (info:Dictionary<String,String>) {
        artistLabel.stringValue = info["artist"]!
        nameLabel.stringValue = info["name"]!
    }
}
```

GUIはStoryboardで作ったが、そちらは割愛する。
Viewのロードが完了したときに` trackInfoController `のDelegateに` self `を設定してやる。
また、`"com.apple.iTunes.playerInfo"`の通知を受け取った時に`trackInfoController`の`onPlay`メソッドを呼び出すようにする。
Delegateから`updateTrackInfo`が呼ばれるとViewのラベルを書き換える。

AppDelegateには特に何も変更を加えていない。

#サーバ実装

##Node.jsのExpressでサクッと作ってしまう

Node.jsはインストール済みとする。

```bash
$ npm install -g express
```

でExpressが入るのでホームディレクトリなどで

```bash
$ express trackinfo
```

としてExpressアプリを作成する。
***

```javascript:trackinfo/routes/index.js
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res) {
	res.render('index', { title: 'Express' });
});

module.exports = router;
```

諸々出来上がった中の`trackinfo/routes/index.js` は上の感じになっている(Version4.2.0)ので、`router.get`の下あたりに以下を追加する。

```javascript:index.js
router.post('/', function(req, res) {
	console.log(req.body);
	res.status(200).send('ok');
});
```

これでPOSTリクエストを処理できる。
***
```bash
$ npm start
```

最後に`npm start`すれば初期設定ではポート3000番でサーバが立ち上がる。

以上でアプリ・サーバの実装が完了。

#結果

![t11MLzwq0c.gif](https://qiita-image-store.s3.amazonaws.com/0/20695/0024b09c-225a-8b38-1a1b-2a4f5386405f.gif "t11MLzwq0c.gif")

#雑感

+ まだObj-CとSwiftの過渡期で、なかなかSwiftで気になったことは調べにくい。特にiOSの情報が多くMacアプリの情報はなかなか見つからない。
+ Delegateを用いたうまい実装法がよくわからず、この形に落ち着いた。AppDelegateを活用したほうがよかっただろうか。
+ `!`とか`?`とか`??`とかなんとなくしかわかってない。

#疑問

通信完了時の`onComplete`が呼ばれている気配がない。どこか間違っているのだろうが、よくわからない。