Carthageの作り方を紐解いてみる~Makefile編~

SwiftLinuxで動かせるようになりはや数ヶ月、そろそろSwift製のツールを自分で作って使って行きたいなぁと思っています。

そこでSwift製のプロダクトで一番使われているんじゃないか?と思われるCarthageの作り方について調べてみました。

github.com

Makefileを見る

なにはともあれまずはMakefileを読んでみます。

とりあえず $ make install すると使えるようになりそうな雰囲気を感じるので一旦インストールしてみます。

$ make install
rm -f "Carthage.pkg"
rm -f "CarthageKit.framework.zip"
rm -rf "/tmp/Carthage.dst"
xcodebuild -workspace 'Carthage.xcworkspace' -scheme 'carthage' DSTROOT=/tmp/Carthage.dst clean
Build settings from command line:
    DSTROOT = /tmp/Carthage.dst
〜(中略)〜
Sudo installer -pkg Carthage.pkg -target /
Password:
installer: Package name is Carthage
installer: Installing at base path /
installer: The install was successful.

xcodebuildpkgbuildが走り、生成物であるCarthage.pkgをインストールしました。

$ carthage
Available commands:

   archive           Archives built frameworks into a zip that Carthage can use
   bootstrap         Check out and build the project's dependencies
   build             Build the project's dependencies
   checkout          Check out the project's dependencies
   copy-frameworks   In a Run Script build phase, copies each framework specified by a SCRIPT_INPUT_FILE environment variable into the built app bundle
   fetch             Clones or fetches a Git repository ahead of time
   help              Display general or command-specific help
   update            Update and rebuild the project's dependencies
   version           Display the current version of Carthage

正しくインストールされていますね。

中で何が起きているか追うためにMakefileの依存関係を整理してみます。

install

install: package
    sudo installer -pkg Carthage.pkg -target /

installpackageに依存していて、最終的にinstallerを走らせてCarthage.pkgをインストールするためのターゲットのようです。

package

package: installables
    pkgbuild \
        --component-plist "$(COMPONENTS_PLIST)" \
        --identifier "org.carthage.carthage" \
        --install-location "/" \
        --root "$(TEMPORARY_FOLDER)" \
        --version "$(VERSION_STRING)" \
        "$(OUTPUT_PACKAGE)"

    (cd "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)" && zip -q -r --symlinks - "$(OUTPUT_FRAMEWORK)") > "$(OUTPUT_FRAMEWORK_ZIP)"

packageinstallablesに依存していて、pkgbuildCarthage.pkgを生成するためのターゲットのようです。

引数のTEMPORARY_FOLDERにはinstallablesでビルドされた生成物が配置されてそうです。

ちなみに最終行のCarthageKit.framework.zipは何目的で作っているのかよくわかりませんでした。

intallables

installables: clean bootstrap
    $(BUILD_TOOL) $(XCODEFLAGS) install

    mkdir -p "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)"
    mv -f "$(CARTHAGEKIT_BUNDLE)" "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)/$(OUTPUT_FRAMEWORK)"
    mv -f "$(CARTHAGE_EXECUTABLE)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)/carthage"
    rm -rf "$(BUILT_BUNDLE)"

installablescleanbootstrapに依存していて、pkgbuildで利用するコマンドやフレームワークを生成し、必要な箇所に配置するためのターゲットのようです。

実際に叩かれてるコマンドは xcodebuild -workspace 'Carthage.xcworkspace' -scheme 'carthage' DSTROOT=/tmp/Carthage.dst install で、xcodebuildinstallは指定されたDSTROOTに対してビルドしたターゲットをインストールするアクションです。

今回で言うと /tmp/Carthage.dstCarthageのビルド結果であるCarthageKit.frameworkと、carthageのバイナリ本体を配置しています。

clean & bootstrap

bootstrap:
    script/bootstrap

clean:
    rm -f "$(OUTPUT_PACKAGE)"
    rm -f "$(OUTPUT_FRAMEWORK_ZIP)"
    rm -rf "$(TEMPORARY_FOLDER)"
    $(BUILD_TOOL) $(XCODEFLAGS) clean

cleanは過去の生成物の削除、bootstrapはルート以下のscript/bootstrapを叩いており、どちらも他のターゲットには依存していませんでした。 script/bootstrapは、Carthageプロジェクトが利用している外部ライブラリをgitsubmoduleで管理するためのスクリプトでした。

まとめ

フローを整理します。

  1. 過去の生成物を削除する
  2. Carthageのビルドに必要なライブラリを取得/更新する
  3. xcodebuildを利用してCarthageをビルドし、CarthageKit.frameworkcarthageのバイナリを生成し特定の箇所に配置する
  4. 特定の箇所に配置された生成物をCarthage.pkgにパッケージ化する
  5. Carthage.pkgをインストールする

以上のフローで、Carthageが手元で利用できるようになりました。 次回はCarthageをビルドする周りを詳しく見てみようと思います。

Objective-Cで書かれたframeworkのSwift対応

先日書いた記事 iOSにおける静的ライブラリ作成技法 - 渋谷ラーメン男道 で、iOSにおける静的ライブラリ・frameworkの作成方法について紹介しましたが、あの内容だけだと Swift のプロジェクトからインポートすることが出来なかったのでその辺の話になります。

Swift からObjective-C のソースを利用する方法

Swift から Objective-Cのソースを利用する方法は2種類あります。

  1. Bridging-Headerを利用する
  2. Clang のModuelsを利用する

Bridging-Header

公式のリファレンス Using Swift with Cocoa and Objective-C (Swift 2.1): Swift and Objective-C in the Same Project にもありますが、Bridging-Header.hファイルを利用することで利用可能です。

Swift 対応していないサードパーティライブラリを利用する場合はこの方法が主流じゃないでしょうか。

しかし、この方法はライブラリ提供側ではなく利用側の設定になります。

ライブラリを提供する立場では、次に紹介するModulesを利用して Swift 対応したライブラリを提供することで、利用側でBridging-Headerを設定する手間をなくすことが出来ます。

Modules

Modulesは Clang の3.3から提供されている機能です。

iOSの文脈ではXcode5、iOS7から利用可能になっており、Modulesを利用することで、コンパイル周りが最適化され、アプリケーションのコンパイル速度向上が期待できます。

また、Objective-Cで書かれたライブラリを Module として読み込むことで、Swift から利用可能になります。

今回は、前回の記事で作成した framework を例に、 Module を作成する方法を紹介します。

前回同様こちらのリポジトリで作成したプロジェクトを公開しています。

Swiftプロジェクトの作成

まずは Swift プロジェクトを作成して、前回のままの framework ではインポートできないことを確認します。

f:id:DayBySay:20160124010857p:plain プロジェクトは特に特別な設定を行っていません。

次に framework のリンクを行います。

アプリのターゲット -> Linked Frameworks and LIbraries -> Add Other から前回作成した framework を選択します。 f:id:DayBySay:20160124011041p:plain

framework が追加されました。 f:id:DayBySay:20160124011216p:plain

framewrok のインポートを試してみます f:id:DayBySay:20160124011252p:plain

やはり、現状ではエラーがでてしまうので、Module 化を行います。

framework を Module 化する

作業としては framework 内に Modulesディレクトリを作成し、その下にmodule.modulemapを追加するだけです。 framework の最終的なディレクトリ構成は、公式ページにある通り下記のようになるはずです。

Name.framework/
  Modules/module.modulemap  Module map for the framework
  Headers/                  Subdirectory containing framework headers
  Frameworks/               Subdirectory containing embedded frameworks
  Resources/                Subdirectory containing additional resources
  Name                      Symbolic link to the shared library for the framework

今回はmodule.modulemapファイルは下記のようにしました。

framework module HOGEFuga {
    umbrella header "HOGEFuga.h"

    export *
    module * { export * }
}

上記記述では、HOGEFuga.hをアンブレラヘッダとして追加しているので、HOGEFuga.h内でインポートされているファイルを SubModuleとして読み込む記述になっています。

HOGEFuga.hHOGEFugaService.hをインポートしているので、Swift プロジェクト側で Import HOGEFugaするだけでHOGEFugaServiceクラスを利用できるようになるはずです。

#import <Foundation/Foundation.h>

#import <HOGEFuga/HOGEFugaService.h>

元のプロジェクトではMakefileを使って framework を作成しているのでそれも編集します。 最終的には下記のようにしました。

XCODEBUILD?=$(shell which xcodebuild)
TARGET_NAME?=HOGEFuga
LIB_NAME:=lib${TARGET_NAME}.a
PROJECT_NAME:=${TARGET_NAME}.xcodeproj
BUILD_DIR:=./
CONFIGURATION?=Release
FW_DIR:=${TARGET_NAME}.framework
FW_DIR_RES:=${FW_DIR}/Resources
FW_DIR_HEAD:=${FW_DIR}/Headers
FW_DIR_MOD:=${FW_DIR}/Modules
FW_DIRS:=${FW_DIR_RES} ${FW_DIR_HEAD} ${FW_DIR_MOD}

all: HOGEFuga.framework

HOGEFuga.framework: libHOGEFuga.a module.modulemap
    mkdir -p ${FW_DIRS}
    cp ${TARGET_NAME}/*.h ${FW_DIR_HEAD}/.
    cp ${TARGET_NAME}/Info.plist ${FW_DIR_RES}/.
    cp module.modulemap ${FW_DIR_MOD}/.
    cp $< ${FW_DIR}/${TARGET_NAME}

libHOGEFuga.a: libHOGEFugaPhoneos.a libHOGEFugaPhonesimulator.a
    lipo -create -output $@ $^
    rm -rf build/ Release-*/

libHOGEFugaPhoneos.a:
    ${XCODEBUILD} -target ${TARGET_NAME} -project ${PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphoneos -arch armv7 -arch armv7s -arch arm64 clean build ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}"
    mv ${BUILD_DIR}/${CONFIGURATION}-iphoneos/${LIB_NAME} $@

libHOGEFugaPhonesimulator.a:
    ${XCODEBUILD} -target ${TARGET_NAME} -project ${PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 -arch x86_64 clean build ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}"
    mv ${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${LIB_NAME} $@

clean:
    rm *.a
    rm -rf *.framework

最後に$ makeで framework を作り直します。

Swiftで実装する

Swift の実装は下記のようにしました。

import UIKit
import HOGEFuga

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        HOGEFugaService.show()
    }
}

ビルドしてみます。今度は成功しました!

f:id:DayBySay:20160124014854p:plain

f:id:DayBySay:20160124014936p:plain

実際に処理も成功し、ログが吐かれているのが確認できました。

以上、 Objective-C で書かれた framework のSwift対応でした。

iOSの色系ライブラリ決定版? Chameleon

一人でアプリを作る時に、いわゆるビジュアルのデザインや色使い周りが適当になっちゃう人の救世主的なライブラリを見つけたので紹介。

github.com

Chameleonでは何ができるのか?

いい感じの色を簡単にアプリに設定できる

提供する機能

  • 100% Flat & Gorgeous
    • 厳選された24のフラットなカラーがある
  • Flat Color Schemes
  • Contrasting Text
    • 背景色に合わせた文字色を勝手に選んでくれる
  • Themes
    • 1行のコードでいい感じの色合いをアプリに設定できる
  • Colors From Images
    • 画像からカラースキームを抽出できる
  • Gradient Colors
    • グラデーションをカラー
      • 方向もある

動作環境

  • Xcode6.3以降
  • ObjC -> iOS7
  • Swift -> iOS8

導入

CocoaPodsとCarthageの両方に対応しています。 今回はCarthageでインストール。

$ echo 'github "ViccAlexander/Chameleon"' > Cartfile
$ carthage update --platform iOS

これで準備完了。

使ってみる

グローバルテーマを使う

setGlobalThemeUsingPrimaryColorを利用するとコードを一行書くだけで、コンポーネント群の色を一括指定することができます。

Chameleon.setGlobalThemeUsingPrimaryColor(FlatMint(), withSecondaryColor: FlatBlue(), andContentStyle: UIContentStyle.Contrast)
self.view.backgroundColor = FlatYellow()

f:id:DayBySay:20160118130324p:plain

上記はメインカラーをFlatMint()、セカンダリカラーをFlatBlue、ビューの背景色をFlatYellowに指定しています。

フラット間出てますね!

メインとセカンダリのカラー指定は、特定のコンポーネントに作用して色を変更するようです。

コードを読んだ感じ、下記コンポーネント郡はセカンダリ指定時に色が変更されるようです。

  1. UIButton
  2. UIProgressView
  3. UISlider
  4. UISwitch

ちなみにContentStyleContrastにすると、テキスト系コンポーネントの色を見やすい色に勝手にしてくれて便利でした。 ただし、よしなにしてくれるのは指定した色がChameleonが提供しているFlat & Gorgeousな色の場合のみのようです。

補色を使う

Chameleonには補色を返す機能があります。

上記のグローバルテーマでセカンダリカラー等で補色を返してくれるComplementaryFlatColorOf()を使うといい感じになりそうでした。

let mainColor = FlatPink()
let complementaryColor = ComplementaryFlatColorOf(mainColor)
Chameleon.setGlobalThemeUsingPrimaryColor(mainColor, withSecondaryColor: complementaryColor, andContentStyle: .Contrast)

こちらに関しては、下記のようにFlat & Gorgeous以外の色でも利用可能でした。

let mainColor = UIColor.redColor() // 赤色
let complementaryColor = ComplementaryFlatColorOf(mainColor) // 青緑色

色抽出を使う

Chameleonには下記のように、画像からカラースキーマを抽出する機能もあります。

let averageColor = AverageColorFromImage(UIImage("someImageName")!)

画像コンテンツの色ベースでナビゲーションの色を替えるみたいなアプリを見たことがある気がするので、似たようなことをいくつかやってみました。

f:id:DayBySay:20160118130549p:plainf:id:DayBySay:20160118130558p:plainf:id:DayBySay:20160118130605p:plain

ナビゲーションバーの色がいい感じに変わってる気がします、が、素材のか違いがよくわからないですね。

違いを見るために赤系も入れてみました。

f:id:DayBySay:20160118130602p:plain

Storyboard用アドオン

Storyboard用のパレットがこちらからダウンロードできるので、気に入った人は使ってみると良いかと思われます。

小並感

アプリの色設定をこれで一本化する前提なら結構使えるかなと思います。実際にアプリを作りたいなぁ。

一方、機能が盛りだくさんでただのUIColorクラス拡張ではないので、色指定をHexでやりたいくらいの思いの方にはオススメできなさそうです。

あくまでも、色やデザイン周りを簡単に設定したかったり、とりあえずモックアップを作る時とかに使える感じかなぁ。

MAC OSXでsedした時の sed: RE error: illegal byte sequence エラー

なぜかよくわからんが、OSXsedを使っているとたまにタイトルのエラーに遭遇することがちょこちょこあります。

対処方法はロケールをC(POSIX)に指定することらしいけれど、そもそもなぜこれが発生するのかよくわからなかったので調べてみました。

発生した環境

今回このエラーが発生したのは、特定のディレクトリ以下にあるファイル内の文字列を一括置換しようとしたタイミングでした。 コマンドで言うとこんな感じ。

$ find . -type f | xargs sed -i .bak s/hoge/fuga/

エラーとしてはタイトルの通りsed: RE error: illegal byte sequenceとなっていました。

改めて対象ファイルを見てみる

実際のものは見せられないので、それっぽい再現ですがこんな感じでした。

$ ls -la
total 32
drwxr-xr-x   5 t-sei  2125026295   170  1 13 23:21 .
drwx------+ 24 t-sei  2125026295   816  1 13 23:17 ..
-rw-r--r--   1 t-sei  2125026295  6149  1 13 23:19 .DS_Store
-rw-r--r--   1 t-sei  2125026295     5  1 13 23:20 fuga.txt
-rw-r--r--   1 t-sei  2125026295     5  1 13 23:20 hoge.txt

上から三番目に何か怪しい奴が居ますね。彼をsedに投げてみます

$ sed s/hoge/fuga/ .DS_Store
sed: RE error: illegal byte sequence

ビンゴ!さっそく犯人を見つけました。やはりOSXユーザは.DS_Storeとうまく付き合う方法を学ばねばならないようです。

なぜsed.DS_Storeを渡すとエラーになるのか?

エラー文言を見る限り、不正なバイト列が渡ってきてるんだよねという話のようです。 しかし一方で、ロケールの設定を変更することでこのエラーは発生しなくなりますので、なぜなのか調べてみました。

とりあえずググってみると、UNIX WARE7のsedページでそれっぽい記述を見つけました。

sed環境変数 LC_CTYPE (environ(5) の LANG を参照) に指定されたロケールに従って、script ファイル中の注釈の補助コードセットを処理します。 ただし、後述する y コマンドに関しては例外です。ed(1) で説明しているように、正規表現ではパターン検索をバイト単位ではなく文字単位で行います。

なるほど、LC_ALL(LC_CTYPE)の設定を変えることで挙動が変わる理由がわかりました。

つまり、OSXのデフォルトロケール設定のsedでは.DS_Storeのようなバイナリファイルを解釈出来ないということのようです。

ちなみにLC_CTYPE環境変数OpenGroupのオンラインヘルプによると、 文字などのテキストデータのバイトシーケンスの解釈や文字の分類をする際に参照されるようです。

(追記)挙動と環境変数に関してはOpenGroupの sedページ見るのが一番よさそうだった。

私の環境では下記のようになっていました。

$ locale
LANG="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_CTYPE="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_ALL=

ja_JP.UTF-8を期待しているところにバイナリファイルである.DS_Storeを叩きこまれて解釈出来ずに困ってしまったということでした。

今後、同様のエラーが出た時に参考にしていただければ幸いです。

Server-side Swift, Perfectを使ってHelloWorld ~HTTP Server編~

あけましておめでとうございます。 前回のSwiftLinux環境を作った記事の続きです。

今回のゴールはSwiftフレームワークであるところのPerfectを用いてHTTPレスポンスを返すところがゴールになります。

環境等は前回の続きです。

とりあえずログイン。 今回はgitを利用するので、鍵等はssh-agentを利用していきます。

$ ssh-add ~/.ssh/github用key
$ vagrant ssh

サーバを立ててみる

Perfectのダウンロード

$ git clone git@github.com:PerfectlySoft/Perfect.git

Perfectを利用する場合、まずはサーバとライブラリのビルドをする必要があります。

$ cd Perfect/
$ make

Makefileの中身はこちら。 どうやらPerfectLibMakefilePerfectServerMakefileを叩いているようです。

ちなみにPerfectServerMakefileは下記です。

# Makefile for Perfect Server

TARGET_FCGI = perfectserverfcgi
TARGET_HTTP = perfectserverhttp
DEBUG = -g -Onone -Xcc -DDEBUG=1
OS = $(shell uname)
SWIFTC = swift
SWIFTC_FLAGS = -frontend $(DEBUG) -c -module-cache-path $(MODULE_CACHE_PATH) -emit-module -I /usr/local/lib -I ../PerfectLib/linked/LibEvent \
    -I ../PerfectLib/linked/OpenSSL -I ../PerfectLib/linked/ICU -I ../PerfectLib/linked/SQLite3 -I ../PerfectLib/linked/LinuxBridge
MODULE_CACHE_PATH = /tmp/modulecache
Linux_SHLIB_PATH = $(shell dirname $(shell dirname $(shell which swiftc)))/lib/swift/linux
SHLIB_PATH = -L$($(OS)_SHLIB_PATH)
LFLAGS = $(SHLIB_PATH) -g -lFoundation -lswiftCore -lswiftGlibc /usr/local/lib/PerfectLib.so -Xlinker -rpath -Xlinker $($(OS)_SHLIB_PATH)

all: modulecache $(TARGET_FCGI) $(TARGET_HTTP)


install: all
    ln -sf `pwd`/$(TARGET_FCGI) /usr/local/bin/
    ln -sf `pwd`/$(TARGET_HTTP) /usr/local/bin/

modulecache:
    @mkdir -p $(MODULE_CACHE_PATH)

$(TARGET_FCGI): $(TARGET_FCGI).o
    clang++ $(LFLAGS) $@.o -o $@

$(TARGET_HTTP): $(TARGET_HTTP).o
    clang++ $(LFLAGS) $@.o -o $@

$(TARGET_FCGI).o: main_fcgi.swift
    $(SWIFTC) $(SWIFTC_FLAGS) main.swift $< -o $@ -module-name $(subst .o,,$@) -emit-module-path $(subst .o,,$@).swiftmodule

$(TARGET_HTTP).o: main_http.swift
    $(SWIFTC) $(SWIFTC_FLAGS) main.swift $< -o $@ -module-name $(subst .o,,$@) -emit-module-path $(subst .o,,$@).swiftmodule

clean:
    @rm *.o

どうやらswift -frontendでオブジェクトファイルを生成し、clang++コンパイルしているようです。

makeすると、perfectserverfcgiperfectserverhttpが生成されて、make installするとコマンドのパスを通すところまでやってくれます。

上記コマンドをコンパイルするときにPerfectLibに依存しているため、そちらのビルドが先に必要なようです。ナルホド。

これでサーバを立てる準備が整いましたので、とりあえずHTTPサーバを起動してみます。 下記の通りデフォルトでは8181番ポートに割り当てられています。

$ Perfectserverhttp
Current working directory: /home/vagrant/Perfect/PerfectServer
Starting HTTP server on 0.0.0.0:8181

とりあえずリクエストを投げてみます。

curl localhost:8181
The file "/" was not found.

HTTPレスポンスが返ってきました。どうやら動いてるっぽいですね。

次に出力用のHTMLを用意して動作を確認します。

main_http.swiftファイルを見るとわかりますが、デフォルトのドキュメントルートはプロジェクト直下のwebroot/になっていました。

PerfectServerはHTMLあるいは画像リソース、Mustacheのテンプレートを扱うことができますので、とりあえず適当なHTMLを用意。

$ echo 'Hello World!!' > webroot/index.html

実際にHTMLを解釈してレスポンスが返ってくるか確認しましょう。

$ curl localhost:8181
Hello World!!

正しくレスポンスが返ってきていそうです。非常に簡単ですね。

ルーティング用のスクリプトを書いてみる

PerfectServerのREADMEを読む限り、ハンドラー用のModuleを作成して利用する機能があるようです。

ModuleはPerfectServerModuleInitという関数をもった共有ライブラリとして作成し、PerfectLibrariesディレクトリに配置することで、PerfectServerに登録されます。

PerfectServer.swiftファイルのこの辺りを見るとその様子が伺えます。

また、実際にルーティングを行っているサンプルがこちらにあります。

下記がルーティングの登録部分の抜粋です。

PerfectServerModuleInit()内でRouting.Handler.Register()を呼び出して登録し、RoutingRoutes(RoutMap構造体)に対して、パスと振る舞いを表すクラスを登録することで、動作を定義することができます。

public func PerfectServerModuleInit() {

    // Install the built-in routing handler.
    // Using this system is optional and you could install your own system if desired.
    Routing.Handler.registerGlobally()

    Routing.Routes["GET", ["/", "index.html"] ] = { (_:WebResponse) in return IndexHandler() }
    Routing.Routes["/foo/*/baz"] = { _ in return EchoHandler() }
    Routing.Routes["/foo/bar/baz"] = { _ in return EchoHandler() }
    Routing.Routes["GET", "/user/{id}/baz"] = { _ in return Echo2Handler() }
    Routing.Routes["POST", "/user/{id}/baz"] = { _ in return Echo3Handler() }

    // Check the console to see the logical structure of what was installed.
    print("\(Routing.Routes.description)")
}

振る舞いを表すクラスは、RequestHandlerを継承しhandleRequest()を実装したものとなります。

リクエストを表すWebRequestのオブジェクトが渡ってくるので、それを用いて特定の処理を行い、WebResponserequestCompletedCallback()を呼び出すことでレスポンスを返す事ができます。

サンプルではレスポンスボディに文字列を含めて返すものを実装していました。

class EchoHandler: RequestHandler {

    func handleRequest(request: WebRequest, response: WebResponse) {
        response.appendBodyString("Echo handler: You accessed path \(request.requestURI()) with variables \(request.urlVariables)")
        response.requestCompletedCallback()
    }
}

上記サンプルを参考にして、Hello Worldを返す処理を実装してみます。

まずはModuleのソースを用意します。今回はこんな感じで実装しました。

import PerfectLib

public func PerfectServerModuleInit() {
    Routing.Handler.registerGlobally()

    Routing.Routes["GET", "/"] = { _ in return IndexHandler() }
    print("\(Routing.Routes.description)")
}

class IndexHandler: RequestHandler {

    func handleRequest(request: WebRequest, response: WebResponse) {
        response.appendBodyString("Hello World!! with Request Handler\n")
        response.requestCompletedCallback()
    }
}

次にソースをコンパイルしていきます。

$ swift -frontend -c -module-cache-path /tmp/modulecache -emit-module -I /usr/local/lib -I /home/vagrant/Perfect/PerfectLib/linked/LibEvent -I \
/home/vagrant/Perfect/PerfectLib/linked/OpenSSL_Linux -I /home/vagrant/Perfect/PerfectLib/linked/ICU \
-I /home/vagrant/Perfect/PerfectLib/linked/SQLite3 -I /home/vagrant/Perfect/PerfectLib/linked/LinuxBridge \
"URLHandlers.swift" -o URLRouting.o -module-name URLRouting -emit-module-path URLRouting.swiftmodule
$ clang++ -L/home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/lib/swift/linux \
-lFoundation -lswiftCore -lswiftGlibc /usr/local/lib/PerfectLib.so \
-Xlinker -rpath -Xlinker /home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/lib/swift/linux \
-shared URLRouting.o -o URLRouting.so

結果、下記のファイル群が生成されました。

drwxrwxr-x 2 vagrant vagrant  4096 Jan  8 02:23 ./
drwxrwxr-x 3 vagrant vagrant  4096 Jan  8 01:31 ../
-rw-rw-r-- 1 vagrant vagrant   395 Jan  8 02:23 URLHandlers.swift
-rw-rw-r-- 1 vagrant vagrant  9624 Jan  8 02:23 URLRouting.o
-rwxrwxr-x 1 vagrant vagrant 20003 Jan  8 02:23 URLRouting.so*
-rw-rw-r-- 1 vagrant vagrant 27924 Jan  8 02:23 URLRouting.swiftmodule

このうち、共有ファイルであるところのURLRouting.soをプロジェクト直下の PerfectLibrariesディレクトリにつっこんでperfectserverhttpを起動すれば動作が確認できます。

mkdir -p /home/vagrant/Perfect/PerfectLibraries/
cp URLRouting.so /home/vagrant/Perfect/PerfectLibraries/.

サーバを立てなおして動作確認をします

$ perfectserverhttp
$ curl localhost:8181
Hello World!! with Request Handler

作成したモジュールが読み込まれて、文字列の出力が確認できました。

次回はPerfectが採用しているテンプレートエンジンであるところのMustacheあたりを触ってまとめようかと思っています。

Server-side Swift, Perfectを使ってHelloWorld ~環境構築編~

年の瀬ですね。今年のブログ納め(3回しか書いてないけど)をしたいと思います。

皆さん御存知の通り、iOSネイティブアプリケーション開発に利用できる新言語であるところのSwiftが、OSSとして公開されたタイミングでLinux対応をしてくれたわけですが、さらに、サーバサイドのアプリケーションをSwiftで実現するためのフレームワークの開発が現在いろいろと進んでいます。(TaylorSwiftraSwiftyなど

その中でも一番注目度が高いのが、Perfectです。 github.com

その他のフレームワークに比べ、機能の多さやドキュメントの充実の仕方が頭一つ抜けている印象を受けます。

今回はこちらのPerfectを使ってHello Worldをするための環境構築について書いていきます。 方法としてはは、MacOSXの上にVagrantを用いてLinux環境を作り、そこにSwiftをインストールする形です。

開発環境を用意する

今回はUbuntuの14.04を利用したいと思います。

イメージの用意とセットアップ。

$ vagrant box add ubuntu14.04 https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
$ mkdir SwiftSandbox
$ cd SwiftSandbox
$ vagrant init ubuntu14.04
$ vagrant up

うまいこと作れたらUbuntu環境に接続します。

$ vagrant ssh

Swiftの実行環境を作成する

基本的には公式にある通りに進めれば良いと思います。

まずは必要なライブラリなどのインストール。

$ sudo apt-get update
$ sudo apt-get install clang libicu-dev

Swift本体と署名をダウンロード。

$ mkdir swift
$ cd swift
$ wget https://swift.org/builds/ubuntu1404/swift-2.2-SNAPSHOT-2015-12-21-a/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz
$ wget https://swift.org/builds/ubuntu1404/swift-2.2-SNAPSHOT-2015-12-21-a/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz.sig

署名確認周り。

$ wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import - 
$ gpg --keyserver hkp://pool.sks-keyservers.net --refresh-keys Swift 
$ gpg --verify swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz.sig

本体を解凍してパスを通す。

$ tar xzf swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz
$ export PATH=/home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/bin:"${PATH}" //ここはとりえあずなので、お好みの場所に置いてください

動かしてみる。

$ swift
Welcome to Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c). Type :help for assistance.
  1> let hello = "world!"
hello: String = "world!"
  2> print (hello)
world!

これでSwiftの実行環境の構築完了です。 次回はPerfectを利用してのアプリケーション構築を行ってみたいと思います。

Xcodeのグループとフォルダ参照の違い

XocdeにSDKをドラッグドロップしたんだけど、なぜか参照がうまくいかないみたいなエラーで悩まされた。 エラーで言うと

Undefined symbols for architecture arm64:
  "_OBJC_CLASS_$_******", referenced from:
      type metadata accessor for __ObjC****** in ******.o 

こんな感じ。

何かなーと思ってたけど、結論SDKをフォルダ参照で追加していたのが問題だった・・

f:id:DayBySay:20151225225717p:plain ↑デフォルトは Create group なんだと思うけど、変わっていた・・

グループとフォルダ参照の違いについてはこちらのスライドですごくよくまとまっているので割愛。

自分でいつのまにか変えてしまっていたっぽいので、ドラッグドロップした時にフォルダの色が青くなってたらご用心ください。

f:id:DayBySay:20151225230145p:plain

※茶色いディレクトリアイコンがグループで青いディレクトリアイコンがフォルダ参照