なぜ作ったか
社内にBluetoothで接続できるスピーカーが設置されたので、曲をかけている人が何を再生しているのか共有できたら面白いと思って作った。
最終的にHubotにPOSTしてSlackの特定チャンネルに流すことを想定している。
おことわり
普段はJavascriptを書いている人間なので、Macアプリの綺麗な実装法などは理解していない。もちろんSwiftを触ったのもはじめてである。
アプリ実装
TrackInfoController
このクラスはiTunesの通知を受け取る。そのアーティスト名と曲名をViewに送り、またサーバに送信することを担当する。
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
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はインストール済みとする。
$ npm install -g express
でExpressが入るのでホームディレクトリなどで
$ express trackinfo
としてExpressアプリを作成する。
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
の下あたりに以下を追加する。
router.post('/', function(req, res) {
console.log(req.body);
res.status(200).send('ok');
});
これでPOSTリクエストを処理できる。
$ npm start
最後にnpm start
すれば初期設定ではポート3000番でサーバが立ち上がる。
以上でアプリ・サーバの実装が完了。
結果
雑感
- まだObj-CとSwiftの過渡期で、なかなかSwiftで気になったことは調べにくい。特にiOSの情報が多くMacアプリの情報はなかなか見つからない。
- Delegateを用いたうまい実装法がよくわからず、この形に落ち着いた。AppDelegateを活用したほうがよかっただろうか。
-
!
とか?
とか??
とかなんとなくしかわかってない。
疑問
通信完了時のonComplete
が呼ばれている気配がない。どこか間違っているのだろうが、よくわからない。