Objective-Cで書いたinstancetypeを返すメソッドがswiftから実行出来ない場合の対処

SwiftとObjective-Cが共存するプロジェクトでAPI呼び出し部分で下記のような問題に直面しました。

Objective-Cで実装されている下記のようなinstancetype型を返すクラスメソッドを
Swiftから呼び出しを行おうとすると、コンパイラエラーが発生しました。

Objective-C interfface

@interface MyBook: NSObject
+ (instancetype)book;
@end

Swift 呼び出し

let mybook = MyBook.book()
'book()' is unavailable: use object construction 'MyBook()'

なぜエラーになるか?

bookメソッドが下記条件を満たしているため、 コンパイラにはconvinience methodとして認識されてしまい、コンパイルエラーを吐いてしまっているようです。

  1. 引数を取っていない関数である
  2. クラスの派生的な命名となっている

たとえば、下記のようなクラス名でinterfaceを持つ場合、

@interface LegacySomething: NSObject
+ (instancetype)something;
+ (instancetype)thing;
@end

Swiftで実行すると下記の結果となります。

let mySomething = LegacySomething.something() ## Error
let myThing = LegacySomething.thing() ## Success

解決するには

上記サンプルにもあるように命名を変更するだけで対応することが可能です。
たとえば、UIApplicationで利用されているようなsharedApplicationのような命名は有効で、 コンパイラはこれがconvinienceメソッドだと認識しなくなるようです。

@interface UIApplication: NSObject
+ (instancetype)sharedApplication;
@end
let application = UIApplication.sharedApplication()

今回はinterfaceを拡張して無事Swiftから実行できるようになりました🎉

@interface MyBook: NSObject
+ (instancetype)book;
+ (instancetype)sharedBook;
@end

ref