ヴァル研究所 Advent Calendar 2017 3日目の記事です。
今日は駅すぱあとWebサービスをLinux上のSwiftから扱ってみる話をしようと思います。
LinuxにSwiftをインストールする
お手軽に環境を構築したいので、Linux+Docker上にSwift環境を構築します。
環境はホスト、ゲスト(Dockerコンテナ)共に Ubuntu 16.04.2 LTS
を使用しています。
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
Docker上にLinux環境を作成する
以下の手順でUbuntuのDockerコンテナを起動します。Swiftの動作に必要なパッケージとして clang
libpython2.7
libxml2
libcurl3
をインストールしています。 git
はSwiftPMを利用したい場合に必要となります。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 7b9b13f7b9c0 6 months ago 118.3 MB
$
$ docker run -ti --privileged --name swift_env -d 7b9b13f7b9c0 /sbin/init
712906ddad7b3535eabfae0261eb00476455379fa5f9b81435c18938e388d24a
$ docker exec -ti swift_env bash
root@712906ddad7b:/# apt-get update -y
root@712906ddad7b:/# apt-get install net-tools vim lv curl -y
root@712906ddad7b:/# apt-get install language-pack-ja-base language-pack-ja
root@712906ddad7b:~# apt-get install clang libpython2.7 libxml2 libcurl3 git
Swiftをインストールする
Download SwiftからLinux版のSwiftバイナリをダウンロードして展開します。
root@712906ddad7b:~# curl -O https://swift.org/builds/swift-4.0.2-release/ubuntu1604/swift-4.0.2-RELEASE/swift-4.0.2-RELEASE-ubuntu16.04.tar.gz
root@712906ddad7b:~# tar zxf swift-4.0.2-RELEASE-ubuntu16.04.tar.gz -C .
上記の例では、カレントディレクトリの直下に swift-4.0.2-RELEASE-ubuntu16.04/usr/...
の形で展開されます。今回は /usr
以下ではなく、 /opt/swift-4.0.2
に配置することにします。
root@712906ddad7b:~# [ ! -d /opt ] && mkdir /opt
root@712906ddad7b:~# mv swift-4.0.2-RELEASE-ubuntu16.04 /opt/swift-4.0.2
あとは .bashrc
にSwiftのパスとライブラリパスを設定して準備は完了です。
export PATH=$PATH:/opt/swift-4.0.2/usr/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATHBRARY_PATH:/opt/swift-4.0.2/usr/lib
swift
コマンドが実行できれば確認完了です。
root@712906ddad7b:~# swift --version
Swift version 4.0.2 (swift-4.0.2-RELEASE)
Target: x86_64-unknown-linux-gnu
Swiftから駅すぱあとWebサービスを呼び出してみる
Swiftから駅すぱあとWebサービスの経路探索を実行してみます。この例では、新宿駅〜高円寺の探索を行っています。
import Foundation
// 駅すぱあとWebサービスのアクセスキーを指定してください。
let accessKey = "<駅すぱあとWebサービスのアクセスキー>"
// 22741 => 新宿, 22671 => 高円寺
let urlStr = "http://api.ekispert.jp/v1/json/search/course/extreme?key=\(accessKey)&answerCount=1&viaList=22741:22671"
let req = URLRequest(url: URL(string: urlStr)!)
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: req) { (data, response, error) in
do {
let object = try JSONSerialization.jsonObject(with: data!, options: []) as! Dictionary<String, Any>
// ResultSet.Course.Route.{Line,Point[]}を順次取り出します。
let top = object["ResultSet"] as! Dictionary<String, Any>
let course = top["Course"] as! Dictionary<String, Any>
let route = course["Route"] as! Dictionary<String, Any>
// Line要素には、路線名と行先の文字列が入っています。
let line = route["Line"] as! Dictionary<String, Any>
// Point[].Stationには、出発駅、到着駅の情報が入っています。
let point = route["Point"] as! Array<Dictionary<String, Any>>
// Point[]要素からStation要素を取り出します。
let line_name = line["Name"] as! String
let from_station = point[0]["Station"] as! Dictionary<String, Any>
let to_station = point[1]["Station"] as! Dictionary<String, Any>
let from_station_name = from_station["Name"] as! String
let to_station_name = to_station["Name"] as! String
// あとは表示するだけです。
print(" * \(from_station_name)")
print(" | \(line_name)")
print(" * \(to_station_name)")
} catch let e {
print(e)
}
}
task.resume()
実行結果は以下になります。
root@712906ddad7b:~# swift sample.swift
* 新宿
| JR中央線・高尾行
* 高円寺
補足:SwiftyJSONでJSONのパースを楽に行いたい...
サンプルプログラムでは、Swiftの標準クラスライブラリのみを用いてJSONのパースを行っていました。
これはだいぶ骨と心が折れる感じなので、SwiftyJSON等のライブラリを使って楽をしたいところです。
Linux環境でSwiftPMを利用してのSwiftyJSONインストールは以下になります。
ただし、私の環境では swift build
時にエラーになってしまいます...ので、以下は作業メモ的な感じです。
root@712906ddad7b:~/sample# swift package init --type=executable
Creating executable package: sample
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/sample/main.swift
Creating Tests/
Package.swift
にSwiftyJSONのパッケージを使用するための設定を追加します。ハマり所として、 dependencies[]
への設定追加だけでなく、 targets[]
内の dependencies
にも dependencies: ["SwiftyJSON"]
の記述が必要な点に注意してください。
この記述が抜けていると、 swift build
時に No such module "SwiftyJSON"
エラーになります。
root@712906ddad7b:~/sample# diff -ur Package.swift.ORIG Package.swift
--- Package.swift.ORIG 2017-12-03 10:55:58.662163611 +0000
+++ Package.swift 2017-12-03 11:36:03.231307500 +0000
@@ -8,12 +8,13 @@
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
+ .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "3.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "sample",
- dependencies: []),
+ dependencies: ["SwiftyJSON"]),
]
)
が、先述の通り、今回試した環境では swift build
でビルドエラーが発生してしまい、SwiftyJSONは扱えない状態となっています...orz。
root@712906ddad7b:~/sample# swift build
Compile Swift Module 'SwiftyJSON' (1 sources)
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:67:101: error: use of undeclared type 'NSErrorPointer'
public init(data: Data, options opt: JSONSerialization.ReadingOptions = .allowFragments, error: NSErrorPointer = nil) {
^~~~~~~~~~~~~~
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:120:23: error: argument labels '(data:)' do not match any available overloads
.flatMap{ JSON(data: $0) } ?? JSON(NSNull())
^ ~~~~~~~~~~
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:120:23: note: overloads for 'JSON' exist with these partially matching parameter lists: (Any), (parseJSON: String), (jsonObject: Any), (array: [JSON]), (dictionary: [String : JSON]), (stringLiteral: StringLiteralType), (extendedGraphemeClusterLiteral: StringLiteralType), (unicodeScalarLiteral: StringLiteralType), (integerLiteral: IntegerLiteralType), (booleanLiteral: BooleanLiteralType), (floatLiteral: FloatLiteralType), (dictionaryLiteral: (String, Any)...), (dictionaryLiteral: [(String, Any)]), (arrayLiteral: Any...), (nilLiteral: ()), (rawValue: Any)
.flatMap{ JSON(data: $0) } ?? JSON(NSNull())
^
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:1323:20: error: cannot convert value of type '[Any]' to type 'NSArray' in coercion
return lhs.rawArray as NSArray == rhs.rawArray as NSArray
~~~~^~~~~~~~
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:1325:20: error: cannot convert value of type '[String : Any]' to type 'NSDictionary' in coercion
return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
~~~~^~~~~~~~~~~~~
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:1343:20: error: cannot convert value of type '[Any]' to type 'NSArray' in coercion
return lhs.rawArray as NSArray == rhs.rawArray as NSArray
~~~~^~~~~~~~
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:1345:20: error: cannot convert value of type '[String : Any]' to type 'NSDictionary' in coercion
return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
~~~~^~~~~~~~~~~~~
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:1363:20: error: cannot convert value of type '[Any]' to type 'NSArray' in coercion
return lhs.rawArray as NSArray == rhs.rawArray as NSArray
~~~~^~~~~~~~
/root/sample/.build/checkouts/SwiftyJSON.git--6185397035495820793/Source/SwiftyJSON.swift:1365:20: error: cannot convert value of type '[String : Any]' to type 'NSDictionary' in coercion
return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
~~~~^~~~~~~~~~~~~
error: terminated(1): /opt/swift-4.0.2/usr/bin/swift-build-tool -f /root/sample/.build/debug.yaml main
まとめ
Swiftから駅すぱあとWebサービスを呼び出す手順を紹介しました。macOSの環境ではなく、Linux上でSwiftを動作させるという方法でしたが参考になればと思います。