【Swift3.0】【Xcode8】【iOS10】 Swift3でなんちゃってHTTPサーバを作ってみました。


iOSでコードを組む場合、クライアントなんかは充実しているように感じます。例えばRESTFulをするHTTPクライアントはサービスペペっと呼べばまぁ良い感じに出来ます。が、サーバはそうもいかない印象があります。まぁ普段使わないし、いろいろ面倒事もありますので・・

更にSwift3になって、サーバ系のライブラリが色々使えなかったりとかしたり（ただ最近は是正されつつあります）。

例えばお外でBLEなデバイスのデータを受信解析とかして、その結果を別なノーパソから取る場合、サーバにした方がむしろ楽な時があります。サーバにしといて、状況をcurlで引っ張ってRで可視化とか、簡単な統計をHTMLにしてブラウザで見たりとかです。個人的には回線とBLEも使える実験用データサーバとしてスマホ（私の場合はiPhone）は優れものだと思います。

そういう事を行うためのHTTPサーバっぽいプログラムを作ってみました。

# で、何ができるの？

なんちゃってHTTPサーバもどきとして、以下の事しかできません。

 * プログラムで生成されたHTMLを表示
 * バンドルされたファイルを転送
 * プログラムで生成したDocumentファイルを転送
 * IPv4でのみ動作します。

上記を実現するため、以下の機能をもっています。

* ファイルの読み書き
* バンドルされたファイルのパス取得
* プログラムで書いてDocumentに入れたファイルのパスを取得
* ファイルアクセス関連で使う UInt8配列とかNSData関係のオブジェクト変換
* 同じくStringと上記オブジェクトとの変換
* 通信と連動してView系を更新する
* socketを使った通信（TCPサーバ）

iOSに慣れた方にとっては、socketを除き上記はしょーもない機能なのかもしれません。が、普段やってない人にとってはSwift3の変更もあって、ハードル高いです。そういう機能もAPIにしてみましたので、慣れない方の参考になると・・・いいな。

# TCPサーバを実現するコード

具体的にどう実現したかは、[GitHubにxcodeのプロジェクトを入れました](https://github.com/gdaigo82/iosSampleServer)ので、そのコードを見てもらった方が早いです。gdSampleServerフォルダにあるswift達の内訳を書きます。

URL
https://github.com/gdaigo82/iosSampleServer

|ファイル名|内容|
|:--|:--|
|ViewController.swift|main的な処理。UIの配置など|
|TinyHTTPServer.swift|以下を利用したHTTPサーバ処理|
|SimpleTCPServer.swift|socketを使ったTCPサーバ|
|SimpleFileService.swift|ファイルアクセス関係|
|SimpleCharacter.swift|画像とかテキストの表示を担当|
|ToolSample.swift|UInt8、NSData、のオブジェクト交換、時刻・IP取得など雑多なツール|

私同様、慣れていない方が何らかの参考にしていただくとか、利用していただければ幸いです。

ちなみにTinyHTTPServer.swiftを見てもらうと分かりますが、HTTPとしてはかなりナメた実装なので、あくまでも参考として下さい（苦笑）

# 評価方法

動かして戴ける方のために、簡単に動かし方を説明します。まずは[gitから落とす](https://github.com/gdaigo82/iosSampleServer)かzipを解凍します。

URL
https://github.com/gdaigo82/iosSampleServer

**次に適当なJPEG画像をsample.jpgとして用意し、それをgdSampleServer/gdSampleServerフォルダに入れます。**

その後、xcodeでビルドし、実機もしくはiOSシミュレータで起動してください。例によってですが[プロ生ちゃん](http://pronama.azurewebsites.net/pronama/)が出てきます。

![ios_server_1.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/8c8f44ab-3956-2e68-ba0c-44554d00a127.png)

＃ちなみに実機ですと、本当は3G回線のIPも出ます（つまり2行表示されます）。上の図では一応消させていただきました。

このように生IPでURLが表示されます。またこのアプリは縦固定としています。で、実際にはLAN内のIPを使う事になるかと思います（図だと10.0.0.4）。LAN内のブラウザからまずはそのまま（図なら 10.0.0.4:8081 ）打ち込むと、ブラウザに時刻が表示され、iOS上の画面も変化します。

■ブラウザ
![ios_server_html.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/57988083-78ba-c841-0742-8a307b6e53fb.png)

■iOS側
![ios_server_2.PNG](https://qiita-image-store.s3.amazonaws.com/0/129334/a7fbd455-dc93-549e-60a0-a65233f001c1.png)

この他にも、画像を出したり、あるいはログ（転送サイズ）を出したりできます。URLはそれぞれ /img と /log です。例えばIPが10.0.0.4ならば以下のURLに対応しています。ポート番号はハードコーディングしてますので、必要ならSwiftいじって修正していただければ。

* 10.0.0.4:8081/img  で画像
* 10.0.0.4:8081/log   でログ（総転送サイズ）
* 10.0.0.4:8081          でHTML（時刻）－というか上２つ以外すべてこれ。

また、プロ生ちゃんをクリックしますと初期画面に戻りますので、IPの再確認とかできます。

という事で、一応Webブラウザでデータが拾えているのが分かるかと思います。

# TIPS

コードを組みにあたり、ひっかかったトコなどをまとめておきます。

### IP取得

いつものstack overflowさんです。以下を参考というかほぼ丸々使わせて戴きました（IPv6は使わないようにしたぐらいですね）

http://stackoverflow.com/questions/30748480/swift-get-devices-ip-address

### socket系をswiftで実現するためのxcode8の操作

特殊なヘッダをプロジェクトに追加する必要があります。以下が参考になりました。但しパスは少し工夫してます（プロジェクトフォルダをこれは直接指定しました）

http://www.sirochro.com/note/swift-bridging-header-generate/

### bind系

手前味噌で恐縮ですが・・

http://qiita.com/GDaigo/items/1fe88a6568fbbbaa43e0

### 時刻取得

手前味噌で恐縮ですが２

http://qiita.com/GDaigo/items/270e6ed6e898e8b8e1c3

### マルチスレッド処理とUIとの連携

一般にGUIを持つシステムでマルチスレッドやると、Viewを管轄するスレッド以外からのUIアクセスについて、気にかける必要があります。iOSもそうです。以下、TinyHTTPServer.swiftから該当処理を抜粋してみました。とりあえずこれで動くようです。

```swift
    // UIと別スレッドをこんな感じで処理させてみました。
    // 
    func mainFunc(label : String) {
        let queue = DispatchQueue(label: label)
        queue.async { // ここでViewControlerのスレッドから切り離します。
            self.serverMainSequence() // HTTPサーバのメイン処理（connectからrecv,sendしてshutdownまで）
            DispatchQueue.main.async { // ここでViewControlerのスレッドに戻ることを期待
                // 戻ったので、viewに対していろいろやらかします。
                var text = (self.tool?.getNowClockString())!
                text += " " + self.count.description + "回目\n"
                text += self.serif!
                self.character?.reaction(serif: text, image: self.image!) // これが表示処理です。
                self.count += 1
                self.mainFunc(label: "gdaigo82.server") // 最後に自分を呼び出します。
            }
        }
    }
```

これ、本来は以下のように処理したいのです。

```swift
while (true)
{
    self.serverMainSequence() // HTTPサーバのメイン処理（connectからrecv,sendしてshutdownまで）
    self.character?.reaction(serif: text, image: self.image!)  // これが表示処理です。
}
```

ただ、実際HTTP系は別スレッドで処理させたかったので、DispatchQueueを使ってみました。どうもqueue.async ｛｝とすると、{}の中は別スレッドになるみたいです。更にその中でDispatchQueue.main.async {} と書くと、その上の処理が終わった際にmainつまりUIを担当するスレッドに切り替えてくれ、結果 {}の中ではUIが操作可能になるっぽいです。

なので、通信系とUI表示を連動させたければ、このような書き方をすれば出来るみたいです。

本件、以下が大変参考になりました。

http://fernweh.jp/b/grandcentraldispatch/

# ライセンス

以下使わせて戴きました。素晴らしいソフトウェアを提供して下さり、ありがとうございます。

 * 本プロダクトは[MITライセンス](https://opensource.org/licenses/MIT)とします。詳細はGitHubを。
 * プロ生ちゃんの画像は[「プロ生ちゃん利用ガイドライン」](http://pronama.azurewebsites.net/pronama/guideline/)に従った利用が必要ですので、ご注意ください。

以上です。