Objective-Cで定義してswiftでextensionしたViewControllerをStoryboardでCustomClassとして指定する際の注意点


はじめに

ややこしいタイトルになってしまいましたが・・・

objcで書かれたiOSアプリを段階的にSwift化していくために、既存のViewControllerをswiftでextensionして、swiftで追加機能を実装する、といったことはよく行われていると思います。

参考:
iOSレガシーコード改善ガイド - extensionパターン
http://www.slideshare.net/kenmaz/clipboards/extension-pattern?rftp=success_toast

そのようなViewControllerをStoryboard上でCustomClassとして指定する際にハマったので、その問題と解決策を紹介します。

問題

以下のような状況があるとします。

  • Objective-CでFooViewControllerを定義
  • FooViewControllerをSwiftのextensionで拡張
  • StoryboardでViewControllerを定義して、そのCustomClassとしてFooViewControllerを設定

実行時にStoryboardからFooViewControllerのインスタンスを生成しようとすると

Unknown class _TtC8MyApp14FooViewController in Interface Builder file.

のようなエラーが出て、FooViewControllerではなく、UIViewControllerのインスタンスが生成されてしまいます。

解決方法

objcで定義してSwiftでextensionしたクラスをStoryboardのCustomClassとして設定すると、InterfaceBuilderによって自動的にModuleが設定されてしまうのが原因です。

StroyboardのSource Code Editorで開いて、以下のように該当の<viewcontroller>定義からcustomModulecustomModuleProviderを削除します。

        <scene sceneID="fT9-Wy-Yne">
            <objects>
-                <viewController id="BHJ-B9-zCx" customClass="FooViewController" customModule="MyApp" customModuleProvider="target" sceneMemberID="viewController">
+                <viewController id="BHJ-B9-zCx" customClass="FooViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="8E8-X3-2Ex"/>

Moduleが以下のようにNoneになれば正しくFooViewControllerをロードすることができます。

類似の問題との関係

類似のエラーケースとしては
http://qiita.com/eidera/items/f6fd3b1088d7bd6fe523
などがありますが、ちょうどこれと逆の問題が起きている、という感じのようです。

Storyboardでは、
- Objcで定義したクラス = Module名は指定してはいけない(=None)
- Swiftで定義したクラス = Module名は指定必須
のように設定しておかないと、正しくクラスがロードできない、ということのようです。

Objcで定義したクラスをswiftでextensionすると、それをSwiftで定義されたものだと誤認識してModule名を自動設定してしまう、というIBのバグなのかもしれません。一応Appleに報告しておこうかと思います。

検証環境

Xcode 8.2.1 (8C1002)