読者です 読者をやめる 読者になる 読者になる

文字っぽいの。

文字を書いています。写真も混ざります。

サーバサイドSwift を Heroku にデプロイする with Kitura

はじめに

この記事では

  • Swift 3.0.2
  • Swift Package Manager
  • Kitura

を利用して、Heroku上で簡単なサーバを動作させる手順を記載します。

Swift PMを使ってプロジェクトをセットアップ

まずは、最新版のXcode8.2をインストールしましょう。その後はシェル上で次のコマンドを叩きます。

$ mkdir kitura-test
$ cd kitura-test
$ swift package init --type executable

これでコードを書く準備が整いました。ディレクトリ構造は次のようになっているはずです。シンプルで安心。

.
├── Package.swift
├── Sources
│   └── main.swift
└── Tests

この段階でもビルドが可能です。

$ swift build
$ ./.build/debug/kitura-test
# Hello, world!

コンソールに Hello, world! が出力されると思います。もし、コードの編集やビルドをXcodeで行いたい場合は、

$ swift package generate-xcodeproj

とすればXcodeプロジェクトが生成されます。

Kituraを導入してローカルにサーバを立てる

Package.swiftを編集して、Kituraを使えるようにします。

import PackageDescription

let package = Package(
    name: "kitura-test",
    dependencies: [
        .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 4)
    ]
)

次に Sources/main.swift を編集します。

import Foundation
import Kitura

let router = Router()

router.get("/") { request, response, next in
    response.send("Hello, World!")
    next()
}

Kitura.addHTTPServer(onPort: 8090, with: router)

Kitura.run()

ここまで編集し終わったら

$ swift build
$ ./.build/debug/kitura-test

を実行しましょう。ライブラリが自動で導入され、ビルドが走ります。 ./.build/debug/kitura-test 実行時に特にログは流れてきませんが、サーバは立っているのでブラウザからlocalhost:8090にアクセスしてみましょう。Hello, World! と表示されればOKです。

Herokuにデプロイする

Herokuにデプロイする前に、PORT番号をよしなにする必要があります。まずは Sources/main.swiftを編集しましょう。

import Foundation
import Kitura

let router = Router()

router.get("/") { request, response, next in
    response.send("Hello, World!")
    next()
}

// Herokuでポート番号が変えられるようにする
let port: Int = Int(ProcessInfo.processInfo.environment["PORT"] ?? "8090") ?? 8090

Kitura.addHTTPServer(onPort: 8090, with: router)

Kitura.run()

次にProcfileを生成します

$ echo web: .build/release/kitura-test > Procfile

さて、それではHerokuにデプロイしていきましょう。

$ git init
$ git add .
$ git commit -m 'first commit'
$ heroku create your-project-name
$ heroku buildpacks:set https://github.com/kylef/heroku-buildpack-swift
$ git push heroku master # ビルドも走るのでちょっと時間かかります
$ heroku open

heroku openをするとブラウザが立ち上がります。画面にHello, World!が表示されれば勝利。サーバサイドSwiftをHerokuで動かすことができました。

補足:ログを流したい人へ

HeliumLoggerというライブラリを利用します。Package.swiftSources/main.swiftを編集します。

import PackageDescription

let package = Package(
    name: "kitura-test",
    dependencies: [
        .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 4),
        .Package(url: "https://github.com/IBM-Swift/HeliumLogger.git", majorVersion: 1, minor: 4)
    ]
)
import Kitura
import Foundation
import HeliumLogger

// HeliumLoggerを初期化
HeliumLogger.use()

let router = Router()
router.get("/") { request, response, next in
    response.send("Hello, World!")
    next()
}

// Herokuでポート番号が変えられるようにする
let port: Int = Int(ProcessInfo.processInfo.environment["PORT"] ?? "8090") ?? 8090

Kitura.addHTTPServer(onPort: port, with: router)

Kitura.run()

これだけです。あとはswift buildをした後でサーバを立ち上げ、アクセスをすると

[2017-01-11T18:20:25.581+09:00] [VERBOSE] [Router.swift:67 init(mergeParameters:)] Router initialized
[2017-01-11T18:20:25.596+09:00] [VERBOSE] [Kitura.swift:70 run()] Starting Kitura framework...
[2017-01-11T18:20:25.596+09:00] [VERBOSE] [Kitura.swift:80 start()] Starting an HTTP Server on port 8090...
[2017-01-11T18:20:25.597+09:00] [INFO] [HTTPServer.swift:85 listen(on:)] Listening on port 8090
[2017-01-11T18:20:43.031+09:00] [VERBOSE] [HTTPIncomingMessage.swift:245 parsingCompleted()] HTTP request from=127.0.0.1; proto=http;

このようにログが吐かれます。便利便利。

最後に

Swift3が開発中だったころのサーバサイドSwiftは、必要で手元で動かすのも大変でしたが、今では少ないコード量で簡単に動かすことができます。

また、Xcodeを利用せずにコードの編集・ビルド・実行ができるので、EmacsVimAtomでSwiftを書きたい人でも安心です。ただし、補完機能についてはSourceKittenを使う必要があり、まだちょっと不便です。

とはいえ、普段利用しているSwiftで簡単にサーバが作れるので、JSONを返すだけの趣味サーバなどは、RubyからSwiftに移行していこうと思います。

仕事机の様子

f:id:FromAtom:20161203212244j:plain

入社して2年目で、もうちょっとしたら3年目になる。時間が進むに連れてオフィスのデスクもカスタマイズされてきたので、備忘録も兼ねて書いておく。

PC

MacBook Pro (Retina, 13-inch, Early 2015) を使ってる。Touch Barついた新しいモデルが欲しいけど、スペック的には問題ないから良いか、という気持ち。

PCの周辺機器

ディスプレイ

ディスプレイは会社から支給されたEIZOのやつを2枚使ってる。HDMIがつながらないので、MacのMini Display Portが両方埋まっていてつらい。ただ、Xcodeが画面いっぱいに表示しても狭いなぁと感じるので、4Kディスプレイとか欲しい気持ちになってきた。

キーボード

キーボードはHHKB Pro2をずっと使ってる。

大学4年生から使ってるので、同じキーボードを4年以上使ってるけどチャタリングも起こさないし、キーがすり減ったりもないので大変良い。

去年からパームレストも使ってる。

木製なのでテカテカしたり手垢が目立ったりしないので良い。

ちなみにHHKB Proを使い続けてからずっと、他のキーボードに浮気せずに生きてきたけど、今はErgo Doxが欲しくてたまらない。

マウス

マウスはLogicoolのAnyWhereシリーズをずっと愛用している。iOSデベロッパーなので、XcodeでStoryboardをいじることが多いんだけど、トラックパッドやトラックボールだと細かい作業がやりにくいのでマウスを使っている。

このマウスは本当に良くて、カーソルがどっかに吹っ飛んでいかないし、摩耗でクリックが効かなくなることもほぼない。ホイールが無抵抗で回るモードがあるんだけど、これがまた便利。ちょっと高いけど、HHKBと同じで、1回買ってしまえば後は電池を変えるだけでずっと使っていられるので、コスパ的にはいい感じ。

便利ツール

Fire HD 7

文頭の写真でキーボードの奥に置かれているタブレット。実際に買ったのはFire HD 7だけど、今はもう売って無いらしい。近いサイズはFire HD 8っぽい。

Fire HD 8 タブレット 16GB、ブラック

Fire HD 8 タブレット 16GB、ブラック

Amazonプレミアム会員だと安く買えたのでサッと買った。これがとにかく便利。YoutubeSpotify、Sound Cloud、Hulu、Amazonビデオ、ニコニコ動画Podcast用のアプリがそろっている。Androidベースなので、困ったらPlay Storeも使えるようにできるらしい。めんどくさいのでしてないけど、特に困っているところはない。

このデバイスで音や動画を流してなにが良いかというと、Macのマシンパワーを一切使わないところ。MacではSlackとChromeXcodeが立ち上がっているので、それだけでメモリとCPUが元気に消費される。それに加えて動画や音楽系のコンテンツを再生すると、だんだんと動作が重くなっていってつらい。

処理能力も解像度も問題ないし、なにより安かったのでいい買い物した。ただ、BluetoothWi-Fiの5GHz帯に対応してないのが厳しい。Fire HD 8だと対応してるっぽい。

タブレットスタンド

Fire HD 7を置いてあるやつ。

角度が変わって便利。ディスプレイの下に置くという構成なので、角度が変えられるのすごい重宝する。

加湿器

オフィスが乾燥しまくってて喉が死にかけたので買った。

このサイズでオフィス全体が潤うわけがないので、横において湯気を吸引する形でなんとかしている。今のところ好調。プラシーボ効果かもしれない。

ペン立て

ペンが立てられて便利です。

ブギーボード

メモ帳代わりに置いてる。「記憶機能とか要らないし、電池切れたら買い直せばいい。」という気持ちで一番安いのを買った。

コーディング中に数式を書いたり、他の人と話をする時に図式で説明できるのでとても便利。紙は折れたり破れたり燃えたりする脆弱性があるので、こういうのを1個持っておくと便利だと思う。

七味

八幡屋礒五郎

長野の七味。ソウルフード。家にもオフィスにも常備しています。

特徴はとてもおいしいことで、利用方法は食べ物にふりかけておいしくすることです。

原了郭 黒七味

京都の七味。

www.hararyoukaku.co.jp

京都の七味は香りが良いのが特徴。辛みが強くなくて、胡麻の香りつよいのが特徴。辛いのが苦手な人でも安心です。

丸亀製麺の生ビール飲み放題に来てる

今6分過ぎてるところでビール2杯目です。これで1000円ってのは良さしかない。

参考

ASCII.jp:丸亀製麺1000円飲み放題セットがコスパ最強|寅年生まれ肉食ナベコの「なんでも食べてみる」

Swiftで与えられたIndexがArrayのindex有効範囲内にあるか確認する

こんな感じ。

let array = [2,3,5,6,9,10]
let index = 0

if 0..<array.count ~= index {
    let value = array[index]
} else {
    print("Out of range: \(index)")
}

indexを適当にいじってみるとわかりやすいです。今回はArrayでやりましたが、 index がある数値範囲にはいっているかを調べるのにも便利です。

エンジニア立ち居振舞い:なんでもかんでも技術で解決しない

お題「エンジニア立ち居振舞い」

面白そうなので便乗する。

だいたい表題通りで、エンジニアはエンジニアリングができてしまうので、エンジニアリング(技術)で解決できる or できないの視点で見がち。 自動化とかスケールするかとかの話も好きなので、自動化できてかつスケールするような仕組みを、新機能を作り始める初期から考えることが多い。

例えば、「今日のおすすめ10選」という毎日更新される機能を実装しようとなった時に、「じゃあLIKEが付いた順と更新日時でいい感じに計算して、バッチ処理でもしましょう。」となる。 ただ、その機能を実装するためには工数がかかるし、そもそも「今日のおすすめ10選」によってKPIにどのくらいの影響があるか分からない。 わからないのにそこに工数を割いてしまうと「なんかすごい時間かかって疲れて作った機能だけどユーザには全然使われない。」という不幸を産んでしまう。

これは正解では無いと思うけど、

  • 簡素な管理画面を作り、当番制で登録
  • 管理画面にあれこれ便利機能を追加
  • 読まれる記事の傾向が分かってきてるはずなのでアルゴリズムを組んで自動化

という手順を踏んでいった方が、無駄に疲れることは減りそう。

エンジニアリングでガッと解決するのはかっこいいし気持ち良いけども、ユーザが得られる幸福量が変わらないのであれば、初期は泥臭く人間の力によって解決していけば良いと思っている。コード量が増えれば増えるほどバグも増えますからね。

なので、なるべく小さな実装で施策や新機能を試せるように、設計や提案の段階で発言できるように考えて立ち居振る舞っている。

※自動化をしなくても良いとか、技術で解決してはいけないという話ではない

Swift3でCornerRadiusが効かない、というか崩れて背景色がおかしくなる。

UITableViewCellの中でよくやってた、

override func layoutSubviews() {
    super.layoutSubviews()

    someView.layer.cornerRadius = someView.frame.height / 2.0
    someView.clipsToBounds = true
}

という書き方だとはViewが崩れたり、角丸にならなかったり、背景色が反映されなかったり(実際は反映されてるんだけどViewのサイズがおかしいからか見えない)する。

こう書けば良い、

override func awakeFromNib() {
    super.awakeFromNib()

    layoutIfNeeded()
    someView.layer.cornerRadius = someView.frame.height / 2.0
    someView.clipsToBounds = true
}

layoutIfNeeded() を先に呼んでおけば良いらしいです。

参考

stackoverflow.com

Swift3でオブジェクトのシリアライズ・デシリアライズがうまくいかなくなった問題の対処

いままではこんなふう required init?(coder aDecoder: NSCoder)に書いていた。

guard let id = aDecoder.decodeObject(forKey: "id") as? Int else {    
    // なんかする
    return
}

Swift3 & Xcode8環境下では、idにずっとnilが返ってきて、デシリアライズができなくて困っていたらこうすればよかった。

let id = aDecoder.decodeInteger(forKey: "id")

ただこの方法だとdecodeに失敗した際に常に0が返ってくる。違う書き方があるのだろうか……。