Swiftでファイル分割後にエラーしたら疑うところ

<!--
swiftに関する情報は世に溢れるほどあるようですが、どういう前提知識を持っているかによって欲しい情報は変わると思うので、ここでは自分のステータスを明記して、「そんな人から見たXCode/swift」というメモを書いてみます。

-  C++とPythonを結構書いた。C++11未満は認めない。
-  UIはあんまり書いたことない。むしろ信号処理とか。numpy大好き。
-  覚えることは少ない方がいいと思っている。


# 基本文法はC++よりも簡単で理にかなってる
C++11をある程度分かってるなら[Swift離陸ガイド](http://www.amazon.co.jp/Swift離陸ガイド-高橋-政明-ebook/dp/B00NBK9KC0) を2時間くらいがんばって読めば基本文法はほぼ理解できると思います。そのくらい文法はシンプル。さすがに新しい言語というだけあって基本静的型付けにも関わらず極めてシンプルで、特にOptional周りとEnum周りの構文はすばらしい。

Swift離陸ガイドを頭から流し読みして、「これはC++/Pythonで言うところのこれだな」とほとんど対応がつくけれど、わずかに検索しないと分からないことがあったのでそれだけチェック。

# 翻訳単位とか名前空間とかアクセス制御が独自
まず境界について。ファイルの境界のmoduleという境界があるようです。前者はC/C++の翻訳単位とほぼ同じ考え方で良いと思います(ただしヘッダという概念はない)。じゃあmoduleは何かというと、どうやらswiftをビルドして(static?) link libraryにした後のモノをさすらしいです。こちらもヘッダはないので、libraryを直importしてるような状態になります。[こちら](http://qiita.com/yimajo/items/56c51228ca29c817f3c1)で詳しい解説あり。

ではこのファイル/moduleを目の前のコードに呼び込むかというと、同一モジュール内別ファイルはまさかの「何もしなくても読める」。でもXCodeの助けが必須になるような…。一方moduleを呼び込む場合はimport hogeとしてhoge.HogeClassと参照するようです。moduleが名前空間の意味も持っている? つまりほぼ100%書かされるimport UIKitは、Appleが用意したUIのmoduleの(static?) link libraryをUIKit.表記で手元に呼び込むという行為になります。

で、もちろん境界を超えられるかどうかという指示があり、swiftでは3種類だそうです。


-->

C++/Pythonな人がswiftに挑んだら門前払いされた記録です。サンプルソースを見つけてきて、中身が長かったのでクラスごとくらいにファイルを分割しようとしたらコンパイルが通らなくなった時のお話。swiftは__同一Xcode target(moduleと呼ぶ)内にいる別ファイルはimport等書かずに使える__らしいので、本当にファイルを分けるならコンパイル結果が変わらないことを期待するのですが、そんな簡単ではありませんでした。

今のところ私は2つのポイントで騙されました。

# 1. privateがC++のprivateではないのでエラー
例えば、以下のclassとfuncは同一ソースにあればコンパイル可, 別ファイルに分けるとコンパイル不可です。ちなみにこのときのエラーメッセージが「'Hoge' does not have a member named 'x'」であり、ほぼ意味不明です。C++er以外にはすんなり受け入れられるのでしょうか。

```swift
    class Hoge{
        private var x : Int = 0
    }

    func someproc(){
        var hoge = Hoge()
        hoge.x = 5 // !!!!!
    }
```

この場合の理由はprivateというアクセスコントロールで、短いソースならすぐ分かります。するとC++erとしてはむしろなぜファイル分割前はコンパイルできたのか理解できなくなるのですが、swiftのアクセスコントロールはC++とは違って以下のようになるらしいです。

-   public: module外からもアクセス可能 (要import文)
-   internal: module内のみ (デフォルト, import文不要)
-   private: __ファイル内のみ__

つまり、__C++と違ってクラスのprivateメンバ(プロパティ)が同一ファイル内のクラス外コードから参照できる__のです。逆から見れば、元々単一のswiftファイルをクラスごとに分けたりした場合にprivateメンバが見れなくなってコンパイルエラーという事件が発生します。自分が書いたコードでなかったので気づくのに時間がかかりました。

# 2. ファイルがCompile SourceではなくBundle Resourceに入ってるからエラー
これは[Swift離陸ガイド](http://www.amazon.co.jp/Swift離陸ガイド-高橋-政明-ebook/dp/B00NBK9KC0) を一度読み込んだ後でも気づくのに2時間かかりました。ここでは「既存の複数クラスが入ったソースを2つに分割する」という操作を追って説明します。Xcode上なら多分メニューのduplicateを使ってコピーしてから、それぞれのファイルで必要な部分だけ残すという操作になるはずです。図で説明すると、まずコピー。

![スクリーンショット 2015-01-31 12.51.46.png](https://qiita-image-store.s3.amazonaws.com/0/56846/f402b754-7d94-6861-c544-5d79e299fd9c.png)

ところがこのダイアログ拡張子が抜けてるので、何も考えずにtest1をtest2に変えてSaveを押すと.swiftが抜けたtest2が出来上がります。

![スクリーンショット 2015-01-31 12.51.56.png](https://qiita-image-store.s3.amazonaws.com/0/56846/12b20d49-2d49-f730-3b5b-b20c712aff7d.png)

となればまぁProject Navigatorからリネームして.swift足しましょう。その後重複部分を消してさあコンパイルとするとこんなエラー。隣のファイルのTest1が見えていません。メッセージはUse of unresolved identifier。普通はtypo等を気にするメッセージだと思います。相変わらず意味不明。

![スクリーンショット 2015-01-31 12.52.30.png](https://qiita-image-store.s3.amazonaws.com/0/56846/ded71bf6-ff81-e480-60fa-1e2283dbb286.png)

ここでさんざん悩むわけですが、途中で気づきました。追加ソースがCompile Sourcesに入っていません。じゃあそもそもコンパイルかからないのでは？という疑念はともかく追加しましょう。

![スクリーンショット 2015-01-31 12.52.49.png](https://qiita-image-store.s3.amazonaws.com/0/56846/b2a04ffd-a20b-48a4-3f41-d574f981306d.png)

追加して、

![スクリーンショット 2015-01-31 12.53.02.png](https://qiita-image-store.s3.amazonaws.com/0/56846/30ee49d9-b0c4-52ee-881d-de38a11d803a.png)

これでよしと思ったらまさかの何も変わらず。

![スクリーンショット 2015-01-31 12.52.30.png](https://qiita-image-store.s3.amazonaws.com/0/56846/ded71bf6-ff81-e480-60fa-1e2283dbb286.png)

根本の問題はこちら。Resourceの方に入っています。この状態だとこのソースはなぜか「コンパイルにかけられるにも関わらず別ファイルの情報は全く見えていない」という謎挙動を示します。ここからtest2.swiftを取り除いたらやっときちんとコンパイルが通るようになりました。

![スクリーンショット 2015-01-31 12.53.23.png](https://qiita-image-store.s3.amazonaws.com/0/56846/3f773ae2-6ac9-9851-3316-7d9a676c7990.png)

さて、お気づきの通り最初のコピーで「.swift」をつけておけば、プロジェクトの設定はこんなことにはならずはじめからコンパイルは通ります。ところがXcodeのUIのせいで問題の操作をしてしまう人はいっぱいいるのではないでしょうか？そして全く原因に気づけない…。

ちなみに私は.swiftをつけてコピーした結果とgit上のdiffを見比べてこの原因を説明できるに至りました。これってつまりプロジェクト管理はやっぱりテキストベースの方が安心だよねって話になるような。もうproject.pbxproj直接いじった方いいような…。

# エラーメッセージひどくないですか
一度clang(C++11)のエラーメッセージに慣れるとどうも不親切なメッセージが受け入れがたいです。せめてdo not調ではなくhave to do調で書いてほしい。