Edited at

Objective-CからSwiftへの移行でバグりやすいポイント

More than 1 year has passed since last update.

最近はSwift2.*→3の移行で非常に苦しんでいるエンジニアの方も多いかと思いますが、まずはObjective-C(以下obj-c)からSwiftへの移行で不具合が発生しやすいポイントをまとめてみました。気づき次第追記したいと思います。他にもあればコメントも大歓迎です :smile:


Optional関連


意図せずwrapされた値を使用してエラー

Intが入っていると思っていたのに、optional(10) のように余計な文字列が含まれている場合があります。optionalで定義した値の場合、unwrapしないと想定した値にならず、思わず不具合を引き起こす恐れがあります。



var badgeCount: Int?

badgeCount = 5
print("\(badgeCount)") // Optional(5)が出力され、警告も出ない


意図しないnil

obj-cの時はnilの値が入っていた場合は基本的にクラッシュはせず処理が続行されていましたが、Swiftの場合Xcodeが補完してくれるがままにunwrapなどしていると、nilが入ってアプリが意図せずクラッシュするという問題に遭遇すると思います。obj-cの時以上に、どのような値が入るかを想定する必要がありそうです。

下記例では、recordクラスのnameというメンバがnilを許可していて、labelにnameをセットするタイミングでunwrapするとエラーが発生する。



let apiClient = ApiClient()

let response = ApiClient().fetchHogeData()
let record = response.records[0]
label.text = record.name! //エラー発生


型の制約

obj-cでは数値といえば、NSInteger/NSUInteger/NSNumberなど種類が多かったと思いますが、swiftの場合は基本的にIntを使うことが多いかと思います。しかし、両言語が混在していて、Swift→objcに渡す場合はNSIntegerなどにcastする必要があります。Swift単体の場合はあまり意識しなくて良いかもしれませんが、移行過程などでは注意する必要がありそうです。

let memberID: Int = 50

let objcClass = objcClass() // memberIDというNSUIntegerのメンバ変数を持っているとする
objcClass.memberID = UInt(memberID) // memberIDをそのまま渡すとエラーになるのでUIntでキャストする必要がある


obj-cでSwiftのクラスを使う場合の制約

obj-cでswiftのクラスを使う場合は、swiftのクラスがNSObjectを継承している必要があります。


moduleのimportでの不具合

スクリーンショット 2016-12-22 16.26.11.png

今までcocoapodsなどのライブラリを使用する場合は、特定の.hファイルをimportしていたと思いますが、swiftからはモジュール指定でimportすることになります。しかし上記のようにimportの候補に表示されるものの、打ち消し線がでているので使えないかと思いきや、特に問題なく使えてしまいます。下記の記事によるとXcodeのバグではないかとコメントされていますが、なんだか気持ちが悪いですね。。

http://stackoverflow.com/questions/36463653/what-is-this-red-line-through-the-xcode-import-auto-suggest


まとめ

Swiftなり色々とすっきりと記述できるようになった反面、obj-cの時と比べると型に関しては厳格になりました。特にXcodeの表示されるunwrap関連の警告を、あまり理解せず"?"や"!"を付けて進めていないでしょうか?特にobj-cとswiftが混在してしまうコードの場合、どうしてもOptional属性をうまく使いながら実装をする必要があります。きちんと理解した上で適切な属性を使ってコードを書かないと後から痛い目に遭いますので、気をつけましょう :sweat_smile:


参考

どこよりも分かりやすいSwiftの"?"と"!"

Swift3のIUO(!)型から属性への変更に考えさせられたこと